Webserver+3d print
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 +