Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: cyclone_crypto/x509.c
- Revision:
- 0:8918a71cdbe9
diff -r 000000000000 -r 8918a71cdbe9 cyclone_crypto/x509.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/x509.c Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,2325 @@
+/**
+ * @file x509.c
+ * @brief X.509 certificate parsing and verification
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include <ctype.h>
+#include "crypto.h"
+#include "x509.h"
+#include "asn1.h"
+#include "oid.h"
+#include "rsa.h"
+#include "dsa.h"
+#include "ecdsa.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha224.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "sha512.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (X509_SUPPORT == ENABLED)
+
+//Common Name OID (2.5.4.3)
+const uint8_t X509_COMMON_NAME_OID[3] = {0x55, 0x04, 0x03};
+//Surname OID (2.5.4.4)
+const uint8_t X509_SURNAME_OID[3] = {0x55, 0x04, 0x04};
+//Serial Number OID (2.5.4.5)
+const uint8_t X509_SERIAL_NUMBER_OID[3] = {0x55, 0x04, 0x05};
+//Country Name OID (2.5.4.6)
+const uint8_t X509_COUNTRY_NAME_OID[3] = {0x55, 0x04, 0x06};
+//Locality Name OID (2.5.4.7)
+const uint8_t X509_LOCALITY_NAME_OID[3] = {0x55, 0x04, 0x07};
+//State Or Province Name OID (2.5.4.8)
+const uint8_t X509_STATE_OR_PROVINCE_NAME_OID[] = {0x55, 0x04, 0x08};
+//Organization Name OID (2.5.4.10)
+const uint8_t X509_ORGANIZATION_NAME_OID[3] = {0x55, 0x04, 0x0A};
+//Organizational Unit Name OID (2.5.4.11)
+const uint8_t X509_ORGANIZATIONAL_UNIT_NAME_OID[3] = {0x55, 0x04, 0x0B};
+//Title OID (2.5.4.12)
+const uint8_t X509_TITLE_OID[3] = {0x55, 0x04, 0x0C};
+//Name OID (2.5.4.41)
+const uint8_t X509_NAME_OID[3] = {0x55, 0x04, 0x29};
+//Given Name OID (2.5.4.42)
+const uint8_t X509_GIVEN_NAME_OID[3] = {0x55, 0x04, 0x2A};
+//Initials OID (2.5.4.43)
+const uint8_t X509_INITIALS_OID[3] = {0x55, 0x04, 0x2B};
+//Generation Qualifier OID (2.5.4.44)
+const uint8_t X509_GENERATION_QUALIFIER_OID[3] = {0x55, 0x04, 0x2C};
+//DN Qualifier OID (2.5.4.46)
+const uint8_t X509_DN_QUALIFIER_OID[3] = {0x55, 0x04, 0x2E};
+//Pseudonym OID (2.5.4.65)
+const uint8_t X509_PSEUDONYM_OID[3] = {0x55, 0x04, 0x41};
+
+//Subject Directory Attributes OID (2.5.29.9)
+const uint8_t X509_SUBJECT_DIRECTORY_ATTR_OID[3] = {0x55, 0x1D, 0x09};
+//Subject Key Identifier OID (2.5.29.14)
+const uint8_t X509_SUBJECT_KEY_ID_OID[3] = {0x55, 0x1D, 0x0E};
+//Key Usage OID (2.5.29.15)
+const uint8_t X509_KEY_USAGE_OID[3] = {0x55, 0x1D, 0x0F};
+//Subject Alternative Name OID (2.5.29.17)
+const uint8_t X509_SUBJECT_ALT_NAME_OID[3] = {0x55, 0x1D, 0x11};
+//Issuer Alternative Name OID (2.5.29.18)
+const uint8_t X509_ISSUER_ALT_NAME_OID[3] = {0x55, 0x1D, 0x12};
+//Basic Constraints OID (2.5.29.19)
+const uint8_t X509_BASIC_CONSTRAINTS_OID[3] = {0x55, 0x1D, 0x13};
+//Name Constraints OID (2.5.29.30)
+const uint8_t X509_NAME_CONSTRAINTS_OID[3] = {0x55, 0x1D, 0x1E};
+//CRL Distribution Points OID (2.5.29.31)
+const uint8_t X509_CRL_DISTR_POINTS_OID[3] = {0x55, 0x1D, 0x1F};
+//Certificate Policies OID (2.5.29.32)
+const uint8_t X509_CERTIFICATE_POLICIES_OID[3] = {0x55, 0x1D, 0x20};
+//Policy Mappings OID (2.5.29.33)
+const uint8_t X509_POLICY_MAPPINGS_OID[3] = {0x55, 0x1D, 0x21};
+//Authority Key Identifier OID (2.5.29.35)
+const uint8_t X509_AUTHORITY_KEY_ID_OID[3] = {0x55, 0x1D, 0x23};
+//Policy Constraints OID (2.5.29.36)
+const uint8_t X509_POLICY_CONSTRAINTS_OID[3] = {0x55, 0x1D, 0x24};
+//Extended Key Usage OID (2.5.29.37)
+const uint8_t X509_EXTENDED_KEY_USAGE_OID[3] = {0x55, 0x1D, 0x25};
+//Freshest CRL OID (2.5.29.46)
+const uint8_t X509_FRESHEST_CRL_OID[3] = {0x55, 0x1D, 0x2E};
+//Inhibit Any-Policy OID (2.5.29.54)
+const uint8_t X509_INHIBIT_ANY_POLICY_OID[3] = {0x55, 0x1D, 0x36};
+
+
+/**
+ * @brief Parse a X.509 certificate
+ * @param[in] data Pointer to the X.509 certificate to parse
+ * @param[in] length Length of the X.509 certificate
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseCertificate(const uint8_t *data, size_t length,
+ X509CertificateInfo *certInfo)
+{
+ error_t error;
+ size_t totalLength;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG("Parsing X.509 certificate...\r\n");
+ //Clear the certificate information structure
+ memset(certInfo, 0, sizeof(X509CertificateInfo));
+
+ //Read the contents of the certificate
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return ERROR_BAD_CERTIFICATE;
+
+ //Point to the very first field
+ data = tag.value;
+ length = tag.length;
+
+ //Parse TBSCertificate structure
+ error = x509ParseTbsCertificate(data, length, &totalLength, certInfo);
+ //Any error to report?
+ if(error)
+ return ERROR_BAD_CERTIFICATE;
+
+ //Point to the next field
+ data += totalLength;
+ length -= totalLength;
+
+ //Parse SignatureAlgorithm structure
+ error = x509ParseSignatureAlgo(data, length, &totalLength, certInfo);
+ //Any error to report?
+ if(error)
+ return ERROR_BAD_CERTIFICATE;
+
+ //Point to the next field
+ data += totalLength;
+ length -= totalLength;
+
+ //Parse SignatureValue structure
+ error = x509ParseSignatureValue(data, length, &totalLength, certInfo);
+ //Any error to report?
+ if(error)
+ return ERROR_BAD_CERTIFICATE;
+
+ //Certificate successfully parsed
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse TBSCertificate structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseTbsCertificate(const uint8_t *data, size_t length,
+ size_t *totalLength, X509CertificateInfo *certInfo)
+{
+ error_t error;
+ size_t n;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing TBSCertificate...\r\n");
+
+ //Read the contents of the TBSCertificate structure
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Save the total length of the field
+ *totalLength = tag.totalLength;
+
+ //The ASN.1 DER encoded tbsCertificate is used as the input to the signature function
+ certInfo->tbsCertificate = data;
+ certInfo->tbsCertificateLen = tag.totalLength;
+
+ //Point to the very first field of the TBSCertificate
+ data = tag.value;
+ length = tag.length;
+
+ //Parse Version field
+ error = x509ParseVersion(data, length, &n, certInfo);
+ //Failed to parse Version field?
+ if(error)
+ return error;
+
+ //Point to the next field
+ data += n;
+ length -= n;
+
+ //Read SerialNumber field
+ error = x509ParseSerialNumber(data, length, &n, certInfo);
+ //Failed to parse SerialNumber field?
+ if(error)
+ return error;
+
+ //Point to the next field
+ data += n;
+ length -= n;
+
+ //Read Signature field
+ error = x509ParseSignature(data, length, &n, certInfo);
+ //Failed to parse Signature field?
+ if(error)
+ return error;
+
+ //Point to the next field
+ data += n;
+ length -= n;
+
+ //Read Issuer field
+ error = x509ParseName(data, length, &n, &certInfo->issuer);
+ //Failed to parse Issuer field?
+ if(error)
+ return error;
+
+ //Point to the next field
+ data += n;
+ length -= n;
+
+ //Read Validity field
+ error = x509ParseValidity(data, length, &n, certInfo);
+ //Failed to parse Validity field?
+ if(error)
+ return error;
+
+ //Point to the next field
+ data += n;
+ length -= n;
+
+ //Read Subject field
+ error = x509ParseName(data, length, &n, &certInfo->subject);
+ //Failed to parse Subject field?
+ if(error)
+ return error;
+
+ //Point to the next field
+ data += n;
+ length -= n;
+
+ //Read SubjectPublicKeyInfo field
+ error = x509ParseSubjectPublicKeyInfo(data, length, &n, certInfo);
+ //Failed to parse Version field?
+ if(error)
+ return error;
+
+ //Point to the next field
+ data += n;
+ length -= n;
+
+ //Read IssuerUniqueID field (optional)
+ error = x509ParseIssuerUniqueId(data, length, &n, certInfo);
+ //Failed to parse Version field?
+ if(error)
+ return error;
+
+ //Point to the next field
+ data += n;
+ length -= n;
+
+ //Read SubjectUniqueID field (optional)
+ error = x509ParseSubjectUniqueId(data, length, &n, certInfo);
+ //Failed to parse Version field?
+ if(error)
+ return error;
+
+ //Point to the next field
+ data += n;
+ length -= n;
+
+ //Read SubjectUniqueID field (optional)
+ error = x509ParseExtensions(data, length, &n, certInfo);
+ //Failed to parse Version field?
+ if(error)
+ return error;
+
+ //No error to report
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Version field
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseVersion(const uint8_t *data, size_t length,
+ size_t *totalLength, X509CertificateInfo *certInfo)
+{
+ error_t error;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing Version...\r\n");
+
+ //Explicit tagging shall be used to encode version
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_CONTEXT_SPECIFIC, 0);
+
+ //The tag does not match the criteria?
+ if(error)
+ {
+ //Assume X.509v1 format
+ certInfo->version = X509_VERSION_1;
+ //Skip the current field
+ *totalLength = 0;
+ //Exit immediately
+ return NO_ERROR;
+ }
+
+ //Save the total length of the field
+ *totalLength = tag.totalLength;
+
+ //Read the inner tag
+ error = asn1ReadTag(tag.value, tag.length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Check length field
+ if(tag.length != 1)
+ return ERROR_INVALID_LENGTH;
+ //Check version field
+ if(tag.value[0] > X509_VERSION_3)
+ return ERROR_INVALID_VERSION;
+
+ //Save certificate version
+ certInfo->version = tag.value[0];
+ //No error to report
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse SerialNumber field
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseSerialNumber(const uint8_t *data, size_t length,
+ size_t *totalLength, X509CertificateInfo *certInfo)
+{
+ error_t error;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing SerialNumber...\r\n");
+
+ //Read the contents of the SerialNumber structure
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Save the total length of the field
+ *totalLength = tag.totalLength;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Get the signature value
+ certInfo->serialNumber = tag.value;
+ certInfo->serialNumberLen = tag.length;
+
+ //No error to report
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Signature field
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseSignature(const uint8_t *data, size_t length,
+ size_t *totalLength, X509CertificateInfo *certInfo)
+{
+ error_t error;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing Signature...\r\n");
+
+ //Read the contents of the Signature structure
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Save the total length of the field
+ *totalLength = tag.totalLength;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Read the inner tag
+ error = asn1ReadTag(tag.value, tag.length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Get the signature algorithm identifier
+ certInfo->signatureAlgo = tag.value;
+ certInfo->signatureAlgoLen = tag.length;
+
+ //Validity field successfully parsed
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Name structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] name Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseName(const uint8_t *data, size_t length,
+ size_t *totalLength, X509Name *name)
+{
+ error_t error;
+ Asn1Tag tag;
+ Asn1Tag attrType;
+ Asn1Tag attrValue;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing Name...\r\n");
+
+ //Read the contents of the Name structure
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Save the total length of the field
+ *totalLength = tag.totalLength;
+
+ //Raw ASN.1 sequence
+ name->rawData = data;
+ name->rawDataLen = tag.totalLength;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //The Name describes a hierarchical name composed of attributes
+ data = tag.value;
+ length = tag.length;
+
+ //Loop through all the attributes
+ while(length > 0)
+ {
+ //Read current attribute
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SET);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Point to the next attribute
+ data += tag.totalLength;
+ length -= tag.totalLength;
+
+ //Read the inner tag
+ error = asn1ReadTag(tag.value, tag.length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Read attribute type
+ error = asn1ReadTag(tag.value, tag.length, &attrType);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&attrType, FALSE,
+ ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Read attribute value
+ error = asn1ReadTag(tag.value + attrType.totalLength,
+ tag.length - attrType.totalLength, &attrValue);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Check the length of the OID
+ if(attrType.length == 3)
+ {
+ //Common Name attribute found?
+ if(!memcmp(attrType.value, X509_COMMON_NAME_OID, 3))
+ {
+ name->commonName = (const char_t *) attrValue.value;
+ name->commonNameLen = attrValue.length;
+ }
+ //Surname attribute found?
+ else if(!memcmp(attrType.value, X509_SURNAME_OID, 3))
+ {
+ name->surname = (const char_t *) attrValue.value;
+ name->surnameLen = attrValue.length;
+ }
+ //Serial Number attribute found?
+ else if(!memcmp(attrType.value, X509_SERIAL_NUMBER_OID, 3))
+ {
+ name->serialNumber = (const char_t *) attrValue.value;
+ name->serialNumberLen = attrValue.length;
+ }
+ //Country Name attribute found?
+ else if(!memcmp(attrType.value, X509_COUNTRY_NAME_OID, 3))
+ {
+ name->countryName = (const char_t *) attrValue.value;
+ name->countryNameLen = attrValue.length;
+ }
+ //Locality Name attribute found?
+ else if(!memcmp(attrType.value, X509_LOCALITY_NAME_OID, 3))
+ {
+ name->localityName = (const char_t *) attrValue.value;
+ name->localityNameLen = attrValue.length;
+ }
+ //State Or Province Name attribute found?
+ else if(!memcmp(attrType.value, X509_STATE_OR_PROVINCE_NAME_OID, 3))
+ {
+ name->stateOrProvinceName = (const char_t *) attrValue.value;
+ name->stateOrProvinceNameLen = attrValue.length;
+ }
+ //Organization Name attribute found?
+ else if(!memcmp(attrType.value, X509_ORGANIZATION_NAME_OID, 3))
+ {
+ name->organizationName = (const char_t *) attrValue.value;
+ name->organizationNameLen = attrValue.length;
+ }
+ //Organizational Unit Name attribute found?
+ else if(!memcmp(attrType.value, X509_ORGANIZATIONAL_UNIT_NAME_OID, 3))
+ {
+ name->organizationalUnitName = (const char_t *) attrValue.value;
+ name->organizationalUnitNameLen = attrValue.length;
+ }
+ //Title attribute found?
+ else if(!memcmp(attrType.value, X509_TITLE_OID, 3))
+ {
+ name->title = (const char_t *) attrValue.value;
+ name->titleLen = attrValue.length;
+ }
+ //Name attribute found?
+ else if(!memcmp(attrType.value, X509_NAME_OID, 3))
+ {
+ name->name = (const char_t *) attrValue.value;
+ name->nameLen = attrValue.length;
+ }
+ //Given Name attribute found?
+ else if(!memcmp(attrType.value, X509_GIVEN_NAME_OID, 3))
+ {
+ name->givenName = (const char_t *) attrValue.value;
+ name->givenNameLen = attrValue.length;
+ }
+ //Initials attribute OID (2.5.4.43)
+ else if(!memcmp(attrType.value, X509_INITIALS_OID, 3))
+ {
+ name->initials = (const char_t *) attrValue.value;
+ name->initialsLen = attrValue.length;
+ }
+ //Generation Qualifier attribute found?
+ else if(!memcmp(attrType.value, X509_GENERATION_QUALIFIER_OID, 3))
+ {
+ name->generationQualifier = (const char_t *) attrValue.value;
+ name->generationQualifierLen = attrValue.length;
+ }
+ //DN Qualifier attribute found?
+ else if(!memcmp(attrType.value, X509_DN_QUALIFIER_OID, 3))
+ {
+ name->dnQualifier = (const char_t *) attrValue.value;
+ name->dnQualifierLen = attrValue.length;
+ }
+ //Pseudonym attribute found?
+ else if(!memcmp(attrType.value, X509_PSEUDONYM_OID, 3))
+ {
+ name->pseudonym = (const char_t *) attrValue.value;
+ name->pseudonymLen = attrValue.length;
+ }
+ }
+ }
+
+ //Name field successfully parsed
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Validity field
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseValidity(const uint8_t *data, size_t length,
+ size_t *totalLength, X509CertificateInfo *certInfo)
+{
+ error_t error;
+ size_t n;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing Validity...\r\n");
+
+ //Read current ASN.1 tag
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Save the total length of the field
+ *totalLength = tag.totalLength;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Point to the very first field of the sequence
+ data = tag.value;
+ length = tag.length;
+
+ //NotBefore field may be encoded as UTCTime or GeneralizedTime
+ error = x509ParseTime(data, length, &n, &certInfo->validity.notBefore);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Point to the next field
+ data += n;
+ length -= n;
+
+ //NotAfter field may be encoded as UTCTime or GeneralizedTime
+ error = x509ParseTime(data, length, &n, &certInfo->validity.notAfter);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Validity field successfully parsed
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse UTCTime or GeneralizedTime structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] dateTime date resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseTime(const uint8_t *data, size_t length,
+ size_t *totalLength, DateTime *dateTime)
+{
+ error_t error;
+ uint_t value;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing Time...\r\n");
+
+ //Read current ASN.1 tag
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Save the total length of the field
+ *totalLength = tag.totalLength;
+
+ //The date may be encoded as UTCTime or GeneralizedTime
+ if(!asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_UTC_TIME))
+ {
+ //Check the length of the UTCTime field
+ if(tag.length < 12)
+ return ERROR_INVALID_SYNTAX;
+
+ //The UTCTime uses a 2-digit representation of the year
+ error = x509ParseInt(tag.value, 2, &value);
+ //Any error to report?
+ if(error)
+ return error;
+
+ //If YY is greater than or equal to 50, the year shall be interpreted
+ //as 19YY. If YY is less than 50, the year shall be interpreted as 20YY
+ if(value >= 50)
+ dateTime->year = 1900 + value;
+ else
+ dateTime->year = 2000 + value;
+
+ //Point to the next field
+ data = tag.value + 2;
+ }
+ else if(!asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_GENERALIZED_TIME))
+ {
+ //Check the length of the GeneralizedTime field
+ if(tag.length < 14)
+ return ERROR_INVALID_SYNTAX;
+
+ //The GeneralizedTime uses a 4-digit representation of the year
+ error = x509ParseInt(tag.value, 4, &value);
+ //Any error to report?
+ if(error)
+ return error;
+
+ //Point to the next field
+ data = tag.value + 4;
+ }
+ else
+ {
+ //The tag does not contain a valid date
+ return ERROR_FAILURE;
+ }
+
+ //Month
+ error = x509ParseInt(data, 2, &value);
+ //Any error to report?
+ if(error)
+ return error;
+
+ //Save the resulting value
+ dateTime->month = value;
+
+ //Day
+ error = x509ParseInt(data + 2, 2, &value);
+ //Any error to report?
+ if(error)
+ return error;
+
+ //Save the resulting value
+ dateTime->day = value;
+
+ //Hours
+ error = x509ParseInt(data + 4, 2, &value);
+ //Any error to report?
+ if(error)
+ return error;
+
+ //Save the resulting value
+ dateTime->hours = value;
+
+ //Minutes
+ error = x509ParseInt(data + 6, 2, &value);
+ //Any error to report?
+ if(error)
+ return error;
+
+ //Save the resulting value
+ dateTime->minutes = value;
+
+ //Seconds
+ error = x509ParseInt(data + 8, 2, &value);
+ //Any error to report?
+ if(error)
+ return error;
+
+ //Save the resulting value
+ dateTime->seconds = value;
+
+ //Milliseconds
+ dateTime->milliseconds = 0;
+
+ //UTCTime or GeneralizedTime field successfully parsed
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse SubjectPublicKeyInfo structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseSubjectPublicKeyInfo(const uint8_t *data, size_t length,
+ size_t *totalLength, X509CertificateInfo *certInfo)
+{
+ error_t error;
+ size_t n;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing SubjectPublicKeyInfo...\r\n");
+
+ //Read SubjectPublicKeyInfo field
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Save the total length of the field
+ *totalLength = tag.totalLength;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Point to the first field
+ data = tag.value;
+ length = tag.length;
+
+ //Read AlgorithmIdentifier field
+ error = x509ParseAlgorithmIdentifier(data, length, &n, certInfo);
+ //Any error to report?
+ if(error)
+ return error;
+
+ //Point to the next field
+ data += n;
+ length -= n;
+
+ //The SubjectPublicKey structure is encapsulated within a bit string
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_BIT_STRING);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //The bit string shall contain an initial octet which encodes
+ //the number of unused bits in the final subsequent octet
+ if(tag.length < 1 || tag.value[0] != 0x00)
+ return ERROR_FAILURE;
+
+#if (RSA_SUPPORT == ENABLED)
+ //RSA algorithm identifier?
+ if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen,
+ RSA_ENCRYPTION_OID, sizeof(RSA_ENCRYPTION_OID)))
+ {
+ //Read RSAPublicKey structure
+ error = x509ParseRsaPublicKey(tag.value + 1, tag.length - 1, certInfo);
+ }
+ else
+#endif
+#if (DSA_SUPPORT == ENABLED)
+ //DSA algorithm identifier?
+ if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen,
+ DSA_OID, sizeof(DSA_OID)))
+ {
+ //Read DSAPublicKey structure
+ error = x509ParseDsaPublicKey(tag.value + 1, tag.length - 1, certInfo);
+ }
+ else
+#endif
+#if (EC_SUPPORT == ENABLED)
+ //EC public key identifier?
+ if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen,
+ EC_PUBLIC_KEY_OID, sizeof(EC_PUBLIC_KEY_OID)))
+ {
+ //Read ECPublicKey structure
+ error = x509ParseEcPublicKey(tag.value + 1, tag.length - 1, certInfo);
+ }
+ else
+#endif
+ //The certificate does not contain any valid public key...
+ {
+ //Report an error
+ error = ERROR_BAD_CERTIFICATE;
+ }
+
+ //Return status code
+ return error;
+}
+
+
+/**
+ * @brief Parse AlgorithmIdentifier structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseAlgorithmIdentifier(const uint8_t *data, size_t length,
+ size_t *totalLength, X509CertificateInfo *certInfo)
+{
+ error_t error;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing AlgorithmIdentifier...\r\n");
+
+ //Read AlgorithmIdentifier field
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Save the total length of the field
+ *totalLength = tag.totalLength;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Point to the first field
+ data = tag.value;
+ length = tag.length;
+
+ //Read algorithm identifier (OID)
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Save the algorithm identifier
+ certInfo->subjectPublicKeyInfo.oid = tag.value;
+ certInfo->subjectPublicKeyInfo.oidLen = tag.length;
+
+ //Point to the next field (if any)
+ data += tag.totalLength;
+ length -= tag.totalLength;
+
+#if (RSA_SUPPORT == ENABLED)
+ //RSA algorithm identifier?
+ if(!asn1CheckOid(&tag, RSA_ENCRYPTION_OID, sizeof(RSA_ENCRYPTION_OID)))
+ {
+ //RSA does not require any additional parameters
+ error = NO_ERROR;
+ }
+ else
+#endif
+#if (DSA_SUPPORT == ENABLED)
+ //DSA algorithm identifier?
+ if(!asn1CheckOid(&tag, DSA_OID, sizeof(DSA_OID)))
+ {
+ //Read DsaParameters structure
+ error = x509ParseDsaParameters(data, length, certInfo);
+ }
+ else
+#endif
+#if (EC_SUPPORT == ENABLED)
+ //EC public key identifier?
+ if(!asn1CheckOid(&tag, EC_PUBLIC_KEY_OID, sizeof(EC_PUBLIC_KEY_OID)))
+ {
+ //Read ECParameters structure
+ error = x509ParseEcParameters(data, length, certInfo);
+ }
+ else
+#endif
+ //The certificate does not contain any valid public key...
+ {
+ //Report an error
+ error = ERROR_BAD_CERTIFICATE;
+ }
+
+ //Return status code
+ return error;
+}
+
+
+/**
+ * @brief Parse RSAPublicKey structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseRsaPublicKey(const uint8_t *data,
+ size_t length, X509CertificateInfo *certInfo)
+{
+#if (RSA_SUPPORT == ENABLED)
+ error_t error;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing RSAPublicKey...\r\n");
+
+ //Read RSAPublicKey structure
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Point to the first field
+ data = tag.value;
+ length = tag.length;
+
+ //Read Modulus field
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Get the modulus
+ certInfo->subjectPublicKeyInfo.rsaPublicKey.n = tag.value;
+ certInfo->subjectPublicKeyInfo.rsaPublicKey.nLen = tag.length;
+
+ //Point to the next field
+ data += tag.totalLength;
+ length -= tag.totalLength;
+
+ //Read PublicExponent field
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Get the public exponent
+ certInfo->subjectPublicKeyInfo.rsaPublicKey.e = tag.value;
+ certInfo->subjectPublicKeyInfo.rsaPublicKey.eLen = tag.length;
+
+ //Successful processing
+ return NO_ERROR;
+#else
+ //Not implemented
+ return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Parse DSA domain parameters
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseDsaParameters(const uint8_t *data,
+ size_t length, X509CertificateInfo *certInfo)
+{
+#if (DSA_SUPPORT == ENABLED)
+ error_t error;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing DSAParameters...\r\n");
+
+ //Read DSAParameters structure
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Point to the first field
+ data = tag.value;
+ length = tag.length;
+
+ //Read the parameter p
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Save the parameter p
+ certInfo->subjectPublicKeyInfo.dsaParams.p = tag.value;
+ certInfo->subjectPublicKeyInfo.dsaParams.pLen = tag.length;
+
+ //Point to the next field
+ data += tag.totalLength;
+ length -= tag.totalLength;
+
+ //Read the parameter q
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Save the parameter q
+ certInfo->subjectPublicKeyInfo.dsaParams.q = tag.value;
+ certInfo->subjectPublicKeyInfo.dsaParams.qLen = tag.length;
+
+ //Point to the next field
+ data += tag.totalLength;
+ length -= tag.totalLength;
+
+ //Read the parameter g
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Save the parameter g
+ certInfo->subjectPublicKeyInfo.dsaParams.g = tag.value;
+ certInfo->subjectPublicKeyInfo.dsaParams.gLen = tag.length;
+
+ //Successful processing
+ return NO_ERROR;
+#else
+ //Not implemented
+ return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Parse DSAPublicKey structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseDsaPublicKey(const uint8_t *data,
+ size_t length, X509CertificateInfo *certInfo)
+{
+#if (DSA_SUPPORT == ENABLED)
+ error_t error;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing DSAPublicKey...\r\n");
+
+ //Read DSAPublicKey structure
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Save the DSA public value
+ certInfo->subjectPublicKeyInfo.dsaPublicKey.y = tag.value;
+ certInfo->subjectPublicKeyInfo.dsaPublicKey.yLen = tag.length;
+
+ //Successful processing
+ return NO_ERROR;
+#else
+ //Not implemented
+ return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Parse ECParameters structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseEcParameters(const uint8_t *data,
+ size_t length, X509CertificateInfo *certInfo)
+{
+#if (EC_SUPPORT == ENABLED)
+ error_t error;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing ECParameters...\r\n");
+
+ //Read namedCurve field
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //The namedCurve field identifies all the required values for a particular
+ //set of elliptic curve domain parameters to be represented by an object
+ //identifier
+ certInfo->subjectPublicKeyInfo.ecParams.namedCurve = tag.value;
+ certInfo->subjectPublicKeyInfo.ecParams.namedCurveLen = tag.length;
+
+ //Successful processing
+ return NO_ERROR;
+#else
+ //Not implemented
+ return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Parse ECPublicKey structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseEcPublicKey(const uint8_t *data,
+ size_t length, X509CertificateInfo *certInfo)
+{
+#if (EC_SUPPORT == ENABLED)
+ //Debug message
+ TRACE_DEBUG(" Parsing ECPublicKey...\r\n");
+
+ //Make sure the EC public key is valid
+ if(!length)
+ return ERROR_BAD_CERTIFICATE;
+
+ //Save the EC public value
+ certInfo->subjectPublicKeyInfo.ecPublicKey.q = data;
+ certInfo->subjectPublicKeyInfo.ecPublicKey.qLen = length;
+
+ //Successful processing
+ return NO_ERROR;
+#else
+ //Not implemented
+ return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Parse IssuerUniqueID structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseIssuerUniqueId(const uint8_t *data, size_t length,
+ size_t *totalLength, X509CertificateInfo *certInfo)
+{
+ error_t error;
+ Asn1Tag tag;
+
+ //No more data to process?
+ if(!length)
+ {
+ //The IssuerUniqueID field is optional
+ *totalLength = 0;
+ //Exit immediately
+ return NO_ERROR;
+ }
+
+ //Explicit tagging is used to encode the IssuerUniqueID field
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_CONTEXT_SPECIFIC, 1);
+ //The tag does not match the criteria?
+ if(error)
+ {
+ //The IssuerUniqueID field is optional
+ *totalLength = 0;
+ //Exit immediately
+ return NO_ERROR;
+ }
+
+ //Save the total length of the field
+ *totalLength = tag.totalLength;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing IssuerUniqueID...\r\n");
+
+ //This field must only appear if the version is 2 or 3
+ if(certInfo->version < X509_VERSION_2)
+ return ERROR_INVALID_VERSION;
+
+ //Successful processing
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse SubjectUniqueID structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseSubjectUniqueId(const uint8_t *data, size_t length,
+ size_t *totalLength, X509CertificateInfo *certInfo)
+{
+ error_t error;
+ Asn1Tag tag;
+
+ //No more data to process?
+ if(!length)
+ {
+ //The SubjectUniqueID field is optional
+ *totalLength = 0;
+ //Exit immediately
+ return NO_ERROR;
+ }
+
+ //Explicit tagging is used to encode the SubjectUniqueID field
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_CONTEXT_SPECIFIC, 2);
+ //The tag does not match the criteria?
+ if(error)
+ {
+ //The SubjectUniqueID field is optional
+ *totalLength = 0;
+ //Exit immediately
+ return NO_ERROR;
+ }
+
+ //Save the total length of the field
+ *totalLength = tag.totalLength;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing SubjectUniqueID...\r\n");
+
+ //This field must only appear if the version is 2 or 3
+ if(certInfo->version < X509_VERSION_2)
+ return ERROR_INVALID_VERSION;
+
+ //Successful processing
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Extensions structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseExtensions(const uint8_t *data, size_t length,
+ size_t *totalLength, X509CertificateInfo *certInfo)
+{
+ error_t error;
+ bool_t critical;
+ const uint8_t *extensionData;
+ size_t extensionLength;
+ Asn1Tag tag;
+ Asn1Tag oidTag;
+
+ //No more data to process?
+ if(!length)
+ {
+ //The Extensions field is optional
+ *totalLength = 0;
+ //Exit immediately
+ return NO_ERROR;
+ }
+
+ //Explicit tagging is used to encode the Extensions field
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_CONTEXT_SPECIFIC, 3);
+ //The tag does not match the criteria?
+ if(error)
+ {
+ //The Extensions field is optional
+ *totalLength = 0;
+ //Exit immediately
+ return NO_ERROR;
+ }
+
+ //Save the total length of the field
+ *totalLength = tag.totalLength;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing Extensions...\r\n");
+
+ //This field must only appear if the version is 3
+ if(certInfo->version < X509_VERSION_3)
+ return ERROR_INVALID_VERSION;
+
+ //Read inner tag
+ error = asn1ReadTag(tag.value, tag.length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //This field is a sequence of one or more certificate extensions
+ data = tag.value;
+ length = tag.length;
+
+ //Loop through the extensions
+ while(length > 0)
+ {
+ //Read current extension
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Point to the next extension
+ data += tag.totalLength;
+ length -= tag.totalLength;
+
+ //Contents of the current extension
+ extensionData = tag.value;
+ extensionLength = tag.length;
+
+ //Read the object identifier
+ error = asn1ReadTag(extensionData, extensionLength, &oidTag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&oidTag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Next item
+ extensionData += oidTag.totalLength;
+ extensionLength -= oidTag.totalLength;
+
+ //Read the Critical flag (if present)
+ error = asn1ReadTag(extensionData, extensionLength, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_BOOLEAN);
+
+ //Check whether the Critical field is present
+ if(!error)
+ {
+ //Make sure the length of the boolean is valid
+ if(tag.length != 1)
+ return ERROR_INVALID_LENGTH;
+
+ //Each extension in a certificate is designated as either
+ //critical or non-critical
+ critical = tag.value[0] ? TRUE : FALSE;
+
+ //Next item
+ extensionData += tag.totalLength;
+ extensionLength -= tag.totalLength;
+ }
+ else
+ {
+ //The extension is considered as non-critical
+ critical = FALSE;
+ }
+
+ //The extension itself is encapsulated in an octet string
+ error = asn1ReadTag(extensionData, extensionLength, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //The current extension matches the BasicConstraints OID?
+ if(!oidComp(oidTag.value, oidTag.length, X509_BASIC_CONSTRAINTS_OID, 3))
+ {
+ //Parse BasicConstraints extension
+ error = x509ParseBasicConstraints(tag.value, tag.length, certInfo);
+ //Any error to report?
+ if(error)
+ return error;
+ }
+ //The current extension matches the KeyUsage OID?
+ else if(!oidComp(oidTag.value, oidTag.length, X509_KEY_USAGE_OID, 3))
+ {
+ //Parse KeyUsage extension
+ }
+ //The current extension matches the ExtendedKeyUsage OID?
+ else if(!oidComp(oidTag.value, oidTag.length, X509_EXTENDED_KEY_USAGE_OID, 3))
+ {
+ //Parse ExtendedKeyUsage extension
+ }
+ //The current extension is marked as critical?
+ else if(critical)
+ {
+ //A certificate-using system must reject the certificate if it encounters
+ //a critical extension it does not recognize or a critical extension that
+ //contains information that it cannot process
+ return ERROR_UNSUPPORTED_EXTENSION;
+ }
+ }
+
+ //Successful processing
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse BasicConstraints structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseBasicConstraints(const uint8_t *data,
+ size_t length, X509CertificateInfo *certInfo)
+{
+ error_t error;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing BasicConstraints...\r\n");
+
+ //The BasicConstraints structure shall contain a valid sequence
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Point to the first item of the sequence
+ data = tag.value;
+ length = tag.length;
+
+ //The cA boolean is optional...
+ if(length > 0)
+ {
+ //The cA boolean indicates whether the certified public key
+ //may be used to verify certificate signatures
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_BOOLEAN);
+
+ //The cA field is present?
+ if(!error)
+ {
+ //The tag should be 1-byte long
+ if(tag.length != 1)
+ return ERROR_INVALID_LENGTH;
+
+ //Get boolean value
+ certInfo->basicConstraints.ca = tag.value ? TRUE : FALSE;
+
+ //Point to the next item
+ data += tag.totalLength;
+ length -= tag.totalLength;
+ }
+ }
+
+ //The pathLenConstraint field is optional...
+ if(length > 0)
+ {
+ //The pathLenConstraint field gives the maximum number of non-self-issued
+ //intermediate certificates that may follow this certificate in a valid
+ //certification path
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //The pathLenConstraint field is not supported
+ certInfo->basicConstraints.pathLenConstraint = 0;
+ }
+
+ //Successful processing
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse SignatureAlgorithm structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseSignatureAlgo(const uint8_t *data, size_t length,
+ size_t *totalLength, X509CertificateInfo *certInfo)
+{
+ error_t error;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing SignatureAlgorithm...\r\n");
+
+ //Read the contents of the SignatureAlgorithm field
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Save the total length of the field
+ *totalLength = tag.totalLength;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Read the inner tag
+ error = asn1ReadTag(tag.value, tag.length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //This field must contain the same algorithm identifier
+ //as the signature field in the TBSCertificate sequence
+ error = asn1CheckOid(&tag, certInfo->signatureAlgo, certInfo->signatureAlgoLen);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //Successful processing
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse SignatureValue field
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseSignatureValue(const uint8_t *data, size_t length,
+ size_t *totalLength, X509CertificateInfo *certInfo)
+{
+ error_t error;
+ Asn1Tag tag;
+
+ //Debug message
+ TRACE_DEBUG(" Parsing SignatureValue...\r\n");
+
+ //Read the contents of the SignatureValue structure
+ error = asn1ReadTag(data, length, &tag);
+ //Failed to decode ASN.1 tag?
+ if(error)
+ return error;
+
+ //Save the total length of the field
+ *totalLength = tag.totalLength;
+
+ //Enforce encoding, class and type
+ error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_BIT_STRING);
+ //The tag does not match the criteria?
+ if(error)
+ return error;
+
+ //The bit string shall contain an initial octet which encodes
+ //the number of unused bits in the final subsequent octet
+ if(tag.length < 1 || tag.value[0] != 0x00)
+ return ERROR_FAILURE;
+
+ //Get the signature value
+ certInfo->signatureValue = tag.value + 1;
+ certInfo->signatureValueLen = tag.length - 1;
+
+ //Successful processing
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Convert string to integer
+ * @param[in] data String containing the representation of an integral number
+ * @param[in] length Length of the string
+ * @param[out] value On success, the function returns the converted integral number
+ * @return Error code
+ **/
+
+error_t x509ParseInt(const uint8_t *data, size_t length, uint_t *value)
+{
+ //Initialize integer value
+ *value = 0;
+
+ //Parse the string
+ while(length > 0)
+ {
+ //Check whether the character is decimal digit
+ if(!isdigit(*data))
+ return ERROR_FAILURE;
+
+ //Convert the string to integer
+ *value = *value * 10 + (*data - '0');
+
+ //Next character
+ data++;
+ length--;
+ }
+
+ //Successful processing
+ return NO_ERROR;
+}
+
+
+/**
+ * @brief Read a RSA public key
+ * @param[in] certInfo X.509 certificate
+ * @param[out] key RSA public key
+ * @return Error code
+ **/
+
+error_t x509ReadRsaPublicKey(const X509CertificateInfo *certInfo, RsaPublicKey *key)
+{
+#if (RSA_SUPPORT == ENABLED)
+ error_t error;
+
+ //The certificate shall contain a valid RSA public key
+ if(!certInfo->subjectPublicKeyInfo.rsaPublicKey.n ||
+ !certInfo->subjectPublicKeyInfo.rsaPublicKey.e)
+ {
+ //Report an error
+ return ERROR_INVALID_KEY;
+ }
+
+ //Convert the modulus to a big number
+ error = mpiReadRaw(&key->n, certInfo->subjectPublicKeyInfo.rsaPublicKey.n,
+ certInfo->subjectPublicKeyInfo.rsaPublicKey.nLen);
+ //Convertion failed?
+ if(error)
+ return error;
+
+ //Convert the public exponent to a big number
+ error = mpiReadRaw(&key->e, certInfo->subjectPublicKeyInfo.rsaPublicKey.e,
+ certInfo->subjectPublicKeyInfo.rsaPublicKey.eLen);
+ //Convertion failed?
+ if(error)
+ return error;
+
+ //Debug message
+ TRACE_DEBUG("RSA public key:\r\n");
+ TRACE_DEBUG(" Modulus:\r\n");
+ TRACE_DEBUG_MPI(" ", &key->n);
+ TRACE_DEBUG(" Public exponent:\r\n");
+ TRACE_DEBUG_MPI(" ", &key->e);
+
+ //Successful processing
+ return NO_ERROR;
+#else
+ //Not implemented
+ return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Read a DSA public key
+ * @param[in] certInfo X.509 certificate
+ * @param[out] key DSA public key
+ * @return Error code
+ **/
+
+error_t x509ReadDsaPublicKey(const X509CertificateInfo *certInfo, DsaPublicKey *key)
+{
+#if (DSA_SUPPORT == ENABLED)
+ error_t error;
+
+ //The certificate shall contain a valid DSA public key
+ if(!certInfo->subjectPublicKeyInfo.dsaParams.p ||
+ !certInfo->subjectPublicKeyInfo.dsaParams.q ||
+ !certInfo->subjectPublicKeyInfo.dsaParams.g ||
+ !certInfo->subjectPublicKeyInfo.dsaPublicKey.y)
+ {
+ //Report an error
+ return ERROR_INVALID_KEY;
+ }
+
+ //Convert the parameter p to a big number
+ error = mpiReadRaw(&key->p, certInfo->subjectPublicKeyInfo.dsaParams.p,
+ certInfo->subjectPublicKeyInfo.dsaParams.pLen);
+ //Convertion failed?
+ if(error)
+ return error;
+
+ //Convert the parameter q to a big number
+ error = mpiReadRaw(&key->q, certInfo->subjectPublicKeyInfo.dsaParams.q,
+ certInfo->subjectPublicKeyInfo.dsaParams.qLen);
+ //Convertion failed?
+ if(error)
+ return error;
+
+ //Convert the parameter g to a big number
+ error = mpiReadRaw(&key->g, certInfo->subjectPublicKeyInfo.dsaParams.g,
+ certInfo->subjectPublicKeyInfo.dsaParams.gLen);
+ //Convertion failed?
+ if(error)
+ return error;
+
+ //Convert the public value to a big number
+ error = mpiReadRaw(&key->y, certInfo->subjectPublicKeyInfo.dsaPublicKey.y,
+ certInfo->subjectPublicKeyInfo.dsaPublicKey.yLen);
+ //Convertion failed?
+ if(error)
+ return error;
+
+ //Debug message
+ TRACE_DEBUG("DSA public key:\r\n");
+ TRACE_DEBUG(" Parameter p:\r\n");
+ TRACE_DEBUG_MPI(" ", &key->p);
+ TRACE_DEBUG(" Parameter q:\r\n");
+ TRACE_DEBUG_MPI(" ", &key->q);
+ TRACE_DEBUG(" Parameter g:\r\n");
+ TRACE_DEBUG_MPI(" ", &key->g);
+ TRACE_DEBUG(" Public value y:\r\n");
+ TRACE_DEBUG_MPI(" ", &key->y);
+
+ //Successful processing
+ return NO_ERROR;
+#else
+ //Not implemented
+ return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief X.509 certificate validation
+ * @param[in] certInfo X.509 certificate to be verified
+ * @param[in] issuerCertInfo Issuer certificate
+ * @return Error code
+ **/
+
+error_t x509ValidateCertificate(const X509CertificateInfo *certInfo,
+ const X509CertificateInfo *issuerCertInfo)
+{
+ error_t error;
+ time_t currentTime;
+ time_t notBefore;
+ time_t notAfter;
+ const HashAlgo *hashAlgo;
+ HashContext *hashContext;
+
+ //Use RSA, DSA or ECDSA signature algorithm?
+#if (RSA_SUPPORT == ENABLED)
+ bool_t rsaSignAlgo = FALSE;
+#endif
+#if (DSA_SUPPORT == ENABLED)
+ bool_t dsaSignAlgo = FALSE;
+#endif
+#if (ECDSA_SUPPORT == ENABLED)
+ bool_t ecdsaSignAlgo = FALSE;
+#endif
+
+ //Retrieve current time
+ currentTime = getCurrentUnixTime();
+
+ //Any real-time clock implemented?
+ if(currentTime != 0)
+ {
+ //Convert NotBefore and NotAfter to Unix timestamps
+ notBefore = convertDateToUnixTime(&certInfo->validity.notBefore);
+ notAfter = convertDateToUnixTime(&certInfo->validity.notAfter);
+
+ //Check the certificate validity period
+ if(currentTime < notBefore || currentTime > notAfter)
+ return ERROR_CERTIFICATE_EXPIRED;
+ }
+
+ //Make sure that the subject and issuer names chain correctly
+ if(certInfo->issuer.rawDataLen != issuerCertInfo->subject.rawDataLen)
+ return ERROR_BAD_CERTIFICATE;
+ if(memcmp(certInfo->issuer.rawData, issuerCertInfo->subject.rawData, certInfo->issuer.rawDataLen))
+ return ERROR_BAD_CERTIFICATE;
+
+ //Ensure that the issuer certificate is a CA certificate
+ if(issuerCertInfo->version >= X509_VERSION_3 && !issuerCertInfo->basicConstraints.ca)
+ return ERROR_BAD_CERTIFICATE;
+
+ //Retrieve the signature algorithm that has been used to sign the certificate
+#if (RSA_SUPPORT == ENABLED && MD5_SUPPORT == ENABLED)
+ if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+ MD5_WITH_RSA_ENCRYPTION_OID, sizeof(MD5_WITH_RSA_ENCRYPTION_OID)))
+ {
+ //MD5 with RSA signature algorithm
+ rsaSignAlgo = TRUE;
+ hashAlgo = MD5_HASH_ALGO;
+ }
+ else
+#endif
+#if (RSA_SUPPORT == ENABLED && SHA1_SUPPORT == ENABLED)
+ if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+ SHA1_WITH_RSA_ENCRYPTION_OID, sizeof(SHA1_WITH_RSA_ENCRYPTION_OID)))
+ {
+ //SHA-1 with RSA signature algorithm
+ rsaSignAlgo = TRUE;
+ hashAlgo = SHA1_HASH_ALGO;
+ }
+ else
+#endif
+#if (RSA_SUPPORT == ENABLED && SHA256_SUPPORT == ENABLED)
+ if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+ SHA256_WITH_RSA_ENCRYPTION_OID, sizeof(SHA256_WITH_RSA_ENCRYPTION_OID)))
+ {
+ //SHA-256 with RSA signature algorithm
+ rsaSignAlgo = TRUE;
+ hashAlgo = SHA256_HASH_ALGO;
+ }
+ else
+#endif
+#if (RSA_SUPPORT == ENABLED && SHA384_SUPPORT == ENABLED)
+ if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+ SHA384_WITH_RSA_ENCRYPTION_OID, sizeof(SHA384_WITH_RSA_ENCRYPTION_OID)))
+ {
+ //SHA-384 with RSA signature algorithm
+ rsaSignAlgo = TRUE;
+ hashAlgo = SHA384_HASH_ALGO;
+ }
+ else
+#endif
+#if (RSA_SUPPORT == ENABLED && SHA512_SUPPORT == ENABLED)
+ if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+ SHA512_WITH_RSA_ENCRYPTION_OID, sizeof(SHA512_WITH_RSA_ENCRYPTION_OID)))
+ {
+ //SHA-512 with RSA signature algorithm
+ rsaSignAlgo = TRUE;
+ hashAlgo = SHA512_HASH_ALGO;
+ }
+ else
+#endif
+#if (DSA_SUPPORT == ENABLED && SHA1_SUPPORT == ENABLED)
+ if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+ DSA_WITH_SHA1_OID, sizeof(DSA_WITH_SHA1_OID)))
+ {
+ //DSA with SHA-1 signature algorithm
+ dsaSignAlgo = TRUE;
+ hashAlgo = SHA1_HASH_ALGO;
+ }
+ else
+#endif
+#if (DSA_SUPPORT == ENABLED && SHA224_SUPPORT == ENABLED)
+ if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+ DSA_WITH_SHA224_OID, sizeof(DSA_WITH_SHA224_OID)))
+ {
+ //DSA with SHA-224 signature algorithm
+ dsaSignAlgo = TRUE;
+ hashAlgo = SHA224_HASH_ALGO;
+ }
+ else
+#endif
+#if (DSA_SUPPORT == ENABLED && SHA256_SUPPORT == ENABLED)
+ if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+ DSA_WITH_SHA256_OID, sizeof(DSA_WITH_SHA256_OID)))
+ {
+ //DSA with SHA-256 signature algorithm
+ dsaSignAlgo = TRUE;
+ hashAlgo = SHA256_HASH_ALGO;
+ }
+ else
+#endif
+#if (ECDSA_SUPPORT == ENABLED && SHA1_SUPPORT == ENABLED)
+ if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+ ECDSA_WITH_SHA1_OID, sizeof(ECDSA_WITH_SHA1_OID)))
+ {
+ //ECDSA with SHA-1 signature algorithm
+ ecdsaSignAlgo = TRUE;
+ hashAlgo = SHA1_HASH_ALGO;
+ }
+ else
+#endif
+#if (ECDSA_SUPPORT == ENABLED && SHA224_SUPPORT == ENABLED)
+ if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+ ECDSA_WITH_SHA224_OID, sizeof(ECDSA_WITH_SHA224_OID)))
+ {
+ //ECDSA with SHA-224 signature algorithm
+ ecdsaSignAlgo = TRUE;
+ hashAlgo = SHA224_HASH_ALGO;
+ }
+ else
+#endif
+#if (ECDSA_SUPPORT == ENABLED && SHA256_SUPPORT == ENABLED)
+ if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+ ECDSA_WITH_SHA256_OID, sizeof(ECDSA_WITH_SHA256_OID)))
+ {
+ //ECDSA with SHA-256 signature algorithm
+ ecdsaSignAlgo = TRUE;
+ hashAlgo = SHA256_HASH_ALGO;
+ }
+ else
+#endif
+#if (ECDSA_SUPPORT == ENABLED && SHA384_SUPPORT == ENABLED)
+ if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+ ECDSA_WITH_SHA384_OID, sizeof(ECDSA_WITH_SHA384_OID)))
+ {
+ //ECDSA with SHA-384 signature algorithm
+ ecdsaSignAlgo = TRUE;
+ hashAlgo = SHA384_HASH_ALGO;
+ }
+ else
+#endif
+#if (ECDSA_SUPPORT == ENABLED && SHA512_SUPPORT == ENABLED)
+ if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+ ECDSA_WITH_SHA512_OID, sizeof(ECDSA_WITH_SHA512_OID)))
+ {
+ //ECDSA with SHA-512 signature algorithm
+ ecdsaSignAlgo = TRUE;
+ hashAlgo = SHA512_HASH_ALGO;
+ }
+ else
+#endif
+ {
+ //The specified signature algorithm is not supported
+ return ERROR_UNSUPPORTED_SIGNATURE_ALGO;
+ }
+
+ //Allocate a memory buffer to hold the hash context
+ hashContext = cryptoAllocMem(hashAlgo->contextSize);
+ //Failed to allocate memory?
+ if(hashContext == NULL)
+ return ERROR_OUT_OF_MEMORY;
+
+ //Digest the TBSCertificate structure using the specified hash algorithm
+ hashAlgo->init(hashContext);
+ hashAlgo->update(hashContext, certInfo->tbsCertificate, certInfo->tbsCertificateLen);
+ hashAlgo->final(hashContext, NULL);
+
+ //Check signature algorithm
+#if (RSA_SUPPORT == ENABLED)
+ if(rsaSignAlgo)
+ {
+ RsaPublicKey publicKey;
+
+ //Initialize RSA public key
+ rsaInitPublicKey(&publicKey);
+
+ //Get the RSA public key
+ error = x509ReadRsaPublicKey(issuerCertInfo, &publicKey);
+
+ //Check status code
+ if(!error)
+ {
+ //Verify RSA signature
+ error = rsassaPkcs1v15Verify(&publicKey, hashAlgo, hashContext->digest,
+ certInfo->signatureValue, certInfo->signatureValueLen);
+ }
+
+ //Release previously allocated resources
+ rsaFreePublicKey(&publicKey);
+ }
+ else
+#endif
+#if (DSA_SUPPORT == ENABLED)
+ if(dsaSignAlgo)
+ {
+ DsaPublicKey publicKey;
+ DsaSignature signature;
+
+ //Initialize DSA public key
+ dsaInitPublicKey(&publicKey);
+ //Initialize DSA signature
+ dsaInitSignature(&signature);
+
+ //Get the DSA public key
+ error = x509ReadDsaPublicKey(issuerCertInfo, &publicKey);
+
+ //Check status code
+ if(!error)
+ {
+ //Read the ASN.1 encoded signature
+ error = dsaReadSignature(certInfo->signatureValue,
+ certInfo->signatureValueLen, &signature);
+ }
+
+ //Check status code
+ if(!error)
+ {
+ //Verify DSA signature
+ error = dsaVerifySignature(&publicKey, hashContext->digest,
+ hashAlgo->digestSize, &signature);
+ }
+
+ //Release previously allocated resources
+ dsaFreePublicKey(&publicKey);
+ dsaFreeSignature(&signature);
+ }
+ else
+#endif
+#if (ECDSA_SUPPORT == ENABLED)
+ if(ecdsaSignAlgo)
+ {
+ const EcCurveInfo *curveInfo;
+ EcDomainParameters params;
+ EcPoint publicKey;
+ EcdsaSignature signature;
+
+ //Initialize EC domain parameters
+ ecInitDomainParameters(¶ms);
+ //Initialize ECDSA public key
+ ecInit(&publicKey);
+ //Initialize ECDSA signature
+ ecdsaInitSignature(&signature);
+
+ //Retrieve EC domain parameters
+ curveInfo = ecGetCurveInfo(issuerCertInfo->subjectPublicKeyInfo.ecParams.namedCurve,
+ issuerCertInfo->subjectPublicKeyInfo.ecParams.namedCurveLen);
+
+ //Make sure the specified elliptic curve is supported
+ error = (curveInfo == NULL) ? ERROR_BAD_CERTIFICATE : NO_ERROR;
+
+ //Check status code
+ if(!error)
+ {
+ //Load EC domain parameters
+ error = ecLoadDomainParameters(¶ms, curveInfo);
+ }
+
+ //Check status code
+ if(!error)
+ {
+ //Retrieve the EC public key
+ error = ecImport(¶ms, &publicKey, issuerCertInfo->subjectPublicKeyInfo.ecPublicKey.q,
+ issuerCertInfo->subjectPublicKeyInfo.ecPublicKey.qLen);
+ }
+
+ //Check status code
+ if(!error)
+ {
+ //Read the ASN.1 encoded signature
+ error = ecdsaReadSignature(certInfo->signatureValue,
+ certInfo->signatureValueLen, &signature);
+ }
+
+ //Check status code
+ if(!error)
+ {
+ //Verify ECDSA signature
+ error = ecdsaVerifySignature(¶ms, &publicKey,
+ hashContext->digest, hashAlgo->digestSize, &signature);
+ }
+
+ //Release previously allocated resources
+ ecFreeDomainParameters(¶ms);
+ ecFree(&publicKey);
+ ecdsaFreeSignature(&signature);
+ }
+ else
+#endif
+ {
+ //The signature algorithm is not supported...
+ error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
+ }
+
+ //Release hash algorithm context
+ cryptoFreeMem(hashContext);
+ //Return status code
+ return error;
+}
+
+#endif
+