Untitled
unknown
plain_text
6 months ago
53 kB
3
Indexable
/* * X.509v3 certificate parsing and processing (RFC 3280 profile) * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. * * Changes: * - added field start and end to struct x509_parser_time * - set start and end fields of struct x509_parser_time in x509_parse_time() */ #include <string.h> #include "x509v3.h" #include "log.h" #include "objects/obj_mac.h" /* strnlen() is defined in armv7a9 toolchain lib at cs_string.h for BlowFish */ #ifndef USE_BLOWFISH size_t strnlen(const char * str, size_t maxlen) { int i = 0; for (i=0; i<maxlen; i++) { if (!str[i]) break; } return i; } #endif /* #ifndef USE_BLOWFISH */ static void x509_reset_name(struct x509_name *name) { size_t i; for (i = 0; i < name->num_attr; i++) { name->attr[i].value = NULL; name->attr[i].type = X509_NAME_ATTR_NOT_USED; name->attr[i].value_size = 0; } name->num_attr = 0; name->email = NULL; name->email_len = 0; name->alt_email = NULL; name->alt_email_len = 0; name->dns = NULL; name->dns_len = 0; name->uri = NULL; name->uri_len = 0; name->ip = NULL; name->ip_len = 0; os_memset(&name->rid, 0, sizeof(name->rid)); } /** * x509_certificate_reset - Free an X.509 certificate * @cert: Certificate to be freed */ void x509_certificate_reset(struct x509_certificate *cert) { if (cert == NULL) return; os_memset(cert, 0, sizeof(*cert)); x509_reset_name(&cert->issuer); x509_reset_name(&cert->subject); } /** * x509_certificate_reset - Free an X.509 certificate chain * @cert: Pointer to the first certificate in the chain */ void x509_certificate_chain_free(struct x509_certificate *cert) { struct x509_certificate *next = NULL; while (cert) { next = cert->next; cert->next = NULL; x509_certificate_reset(cert); cert = next; } } static int x509_whitespace(char c) { return c == ' ' || c == '\t'; } /*TODO: A. Test x509_str_strip_whitespace for all cases*/ static char const* x509_strn_strip_whitespace(char const *a, size_t* plen) { size_t sizTimmedLen = 0; char const *pTrimmed = a; if( NULL == a || NULL == plen ) { return NULL; } if( 0 == *plen ) { return a; } sizTimmedLen = *plen; /*remove spaces at begin of a string*/ while( sizTimmedLen && x509_whitespace(*pTrimmed)) { pTrimmed++; sizTimmedLen--; } /*remove spaces at end of a string*/ while( sizTimmedLen && x509_whitespace(*(pTrimmed + sizTimmedLen))) { sizTimmedLen--; } *plen = sizTimmedLen; return pTrimmed; } /*TODO: A. Test x509_strn_compare for all cases*/ static int x509_strn_compare(const char *a, size_t a_len, const char *b, size_t b_len) { char const *aa = NULL, *bb = NULL; size_t aa_len = a_len; size_t bb_len = b_len; int ret = 0; if ((!a || !a_len) && b) return -1; if (a && (!b || !b_len)) return 1; if (!a && !b) return 0; /* XXX: logically dead code if (!a_len && !b_len) return 0; */ aa = x509_strn_strip_whitespace(a, &aa_len); bb = x509_strn_strip_whitespace(b, &bb_len); if( aa_len < bb_len ) return -1; if( aa_len > bb_len ) return 1; ret = os_strncasecmp(aa, bb, aa_len); return ret; } /** * x509_name_compare - Compare X.509 certificate names * @a: Certificate name * @b: Certificate name * Returns: <0, 0, or >0 based on whether a is less than, equal to, or * greater than b */ int x509_name_compare(struct x509_name *a, struct x509_name *b) { int res = 0; size_t i = 0; if (!a && b) return -1; if (a && !b) return 1; if (!a && !b) return 0; if (a->num_attr < b->num_attr) return -1; if (a->num_attr > b->num_attr) return 1; for (i = 0; i < a->num_attr; i++) { if (a->attr[i].type < b->attr[i].type) return -1; if (a->attr[i].type > b->attr[i].type) return -1; res = x509_strn_compare(a->attr[i].value, a->attr[i].value_size, b->attr[i].value, b->attr[i].value_size); if (res) return res; } res = x509_strn_compare(a->email, a->email_len, b->email, b->email_len); if (res) return res; return 0; } static int x509_parse_algorithm_identifier(const u8 *buf, size_t len, struct x509_algorithm_identifier *id, const u8 **next) { struct asn1_hdr hdr = {0}; const u8 *pos = NULL, *end = NULL; /* * AlgorithmIdentifier ::= SEQUENCE { * algorithm OBJECT IDENTIFIER, * parameters ANY DEFINED BY algorithm OPTIONAL * } */ if (asn1_get_next(buf, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) return X509_PARSE_ERROR_INVALID_SEQUENCE; pos = hdr.payload; end = pos + hdr.length; if (end > buf + len) return X509_PARSE_ERROR_INVALID_HEADER; *next = end; if (asn1_get_oid(pos, end - pos, &id->oid, &pos)) return X509_PARSE_ERROR_INVALID_OID; /* Custom_1, for ECDSA - ASN1 OID: prime256v1 */ asn1_get_oid(pos, end - pos, &id->oid_custom_1, &pos); // TODO : Need to check ECDSA - The error can occur on ECDSA certificate. /* Optional parameters */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL) { // DRK certificate without parameter is exist in only Great. So except it. id->param.length = -1; return X509_PARSE_OK; } switch(hdr.tag) { case ASN1_TAG_NULL : id->param.data = NULL; break; default : id->param.data = hdr.payload; break; } id->param.length = hdr.length; return X509_PARSE_OK; } static int x509_parse_public_key(const u8 *buf, size_t len, struct x509_certificate *cert, const u8 **next) { struct asn1_hdr hdr = {0}; const u8 *pos = NULL, *end = NULL; /* * SubjectPublicKeyInfo ::= SEQUENCE { * algorithm AlgorithmIdentifier, * subjectPublicKey BIT STRING * } */ pos = buf; end = buf + len; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { return -1; } pos = hdr.payload; if (pos + hdr.length > end) return -1; end = pos + hdr.length; *next = end; if (x509_parse_algorithm_identifier(pos, end - pos, &cert->public_key_alg, &pos)) return -1; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_BITSTRING) { return -1; } if (hdr.length < 1) return -1; pos = hdr.payload; if (*pos) { /* * TODO: should this be rejected? X.509 certificates are * unlikely to use such a construction. Now we would end up * including the extra bits in the buffer which may also be * ok. */ } cert->public_key = pos + 1; cert->public_key_len = hdr.length - 1; return 0; } static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, const u8 **next) { struct asn1_hdr hdr = {0}; const u8 *pos = NULL, *end = NULL, *set_pos = NULL, *set_end = NULL, *seq_pos = NULL, *seq_end = NULL; struct asn1_oid oid; char const *val = NULL; memset(&oid, 0, sizeof(oid)); /* * Name ::= CHOICE { RDNSequence } * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue * AttributeTypeAndValue ::= SEQUENCE { * type AttributeType, * value AttributeValue * } * AttributeType ::= OBJECT IDENTIFIER * AttributeValue ::= ANY DEFINED BY AttributeType */ if (asn1_get_next(buf, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { return X509_PARSE_ERROR_INVALID_SEQUENCE; } pos = hdr.payload; if (pos + hdr.length > buf + len) return X509_PARSE_ERROR_INVALID_HEADER; end = *next = pos + hdr.length; while (pos < end) { enum x509_name_attr_type type; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) { x509_reset_name(name); return X509_PARSE_ERROR_INVALID_SET; } set_pos = hdr.payload; pos = set_end = hdr.payload + hdr.length; if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { x509_reset_name(name); return X509_PARSE_ERROR_INVALID_SEQUENCE; } seq_pos = hdr.payload; seq_end = hdr.payload + hdr.length; if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) { x509_reset_name(name); return X509_PARSE_ERROR_INVALID_OID; } if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL) { x509_reset_name(name); return -1; } /* RFC 3280: * MUST: country, organization, organizational-unit, * distinguished name qualifier, state or province name, * common name, serial number. * SHOULD: locality, title, surname, given name, initials, * pseudonym, generation qualifier. * MUST: domainComponent (RFC 2247). */ type = X509_NAME_ATTR_NOT_USED; if (oid.len == 4 && oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) { /* id-at ::= 2.5.4 */ switch (oid.oid[3]) { case 3: /* commonName */ type = X509_NAME_ATTR_CN; break; case 6: /* countryName */ type = X509_NAME_ATTR_C; break; case 7: /* localityName */ type = X509_NAME_ATTR_L; break; case 8: /* stateOrProvinceName */ type = X509_NAME_ATTR_ST; break; case 10: /* organizationName */ type = X509_NAME_ATTR_O; break; case 11: /* organizationalUnitName */ type = X509_NAME_ATTR_OU; break; case 46: /* dnQualifier */ type = X509_NAME_ATTR_DNQUAL; break; } } else if (oid.len == 7 && oid.oid[0] == 1 && oid.oid[1] == 2 && oid.oid[2] == 840 && oid.oid[3] == 113549 && oid.oid[4] == 1 && oid.oid[5] == 9 && oid.oid[6] == 1) { /* 1.2.840.113549.1.9.1 - e-mailAddress */ name->email = (char const *) hdr.payload; name->email_len = hdr.length; continue; } else if (oid.len == 7 && oid.oid[0] == 0 && oid.oid[1] == 9 && oid.oid[2] == 2342 && oid.oid[3] == 19200300 && oid.oid[4] == 100 && oid.oid[5] == 1 && oid.oid[6] == 25) { /* 0.9.2342.19200300.100.1.25 - domainComponent */ type = X509_NAME_ATTR_DC; } else if (oid.len == 7 && oid.oid[0] == 0 && oid.oid[1] == 9 && oid.oid[2] == 2342 && oid.oid[3] == 19200300 && oid.oid[4] == 100 && oid.oid[5] == 1 && oid.oid[6] == 1) { /* 0.9.2342.19200300.100.1.1 - User ID */ type = X509_NAME_ATTR_UID; } if (type == X509_NAME_ATTR_NOT_USED) { continue; } if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) { x509_reset_name(name); return -1; } val = (char const *) hdr.payload; /* Check for broken string*/ if (os_strnlen(val, hdr.length) != hdr.length) { x509_reset_name(name); return -1; } name->attr[name->num_attr].type = type; name->attr[name->num_attr].value = val; name->attr[name->num_attr].value_size = hdr.length; name->num_attr++; } return 0; } static char * x509_name_attr_str(enum x509_name_attr_type type) { switch (type) { case X509_NAME_ATTR_NOT_USED: return "[N/A]"; case X509_NAME_ATTR_DC: return "DC"; case X509_NAME_ATTR_CN: return "CN"; case X509_NAME_ATTR_C: return "C"; case X509_NAME_ATTR_L: return "L"; case X509_NAME_ATTR_ST: return "ST"; case X509_NAME_ATTR_O: return "O"; case X509_NAME_ATTR_OU: return "OU"; case X509_NAME_ATTR_UID: return "UID"; case X509_NAME_ATTR_DNQUAL: return "DNQ"; } return "?"; } /** * x509_name_string - Convert an X.509 certificate name into a string * @name: Name to convert * @buf: Buffer for the string * @len: Maximum buffer length */ void x509_name_string(struct x509_name *name, char *buf, size_t len) { char *pos = NULL, *end = NULL; int ret = 0; size_t i = 0; if (len == 0) return; pos = buf; end = buf + len; for (i = 0; i < name->num_attr; i++) { ret = os_snprintf(pos, end - pos, "%s=%.*s, ", x509_name_attr_str(name->attr[i].type), (int)name->attr[i].value_size, name->attr[i].value); if (ret < 0 || ret >= end - pos) goto done; pos += ret; } if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') { pos--; *pos = '\0'; pos--; *pos = '\0'; } if (name->email) { ret = os_snprintf(pos, end - pos, "/emailAddress=%.*s", (int)name->email_len, name->email); if (ret < 0 || ret >= end - pos) goto done; pos += ret; } done: end[-1] = '\0'; } /*static int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t) { struct tm tm;, *tm1; time_t t_local; time_t t1, t2; os_time_t tz_offset = 0; if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || sec > 60) return -1; memset(&tm, 0, sizeof(tm)); tm.tm_year = year - 1900; tm.tm_mon = month - 1; tm.tm_mday = day; tm.tm_hour = hour; tm.tm_min = min; tm.tm_sec = sec; t_local = mktime(&tm); figure out offset to UTC tm1 = localtime(&t_local); if (tm1) { t1 = mktime(tm1); tm1 = gmtime(&t_local); if (tm1) { t2 = mktime(tm1); tz_offset = t2 - t1; } else tz_offset = 0; } else tz_offset = 0; *t = (os_time_t) t_local - tz_offset; return 0; }*/ static int x509_atoi(const char *str, int len, int* val) { int i = 0, j = 0, power = 0; if (!val) return 0; *val = 0; for (i=0; i<len; i++) { if ((str[i]>='0') && (str[i]<='9')) { power = 1; for (j=0; j<(len - 1 - i); j++) power *= 10; *val += (str[i] - 0x30) * power; } else return 0; } return 1; } /*static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val) { const char *pos; int year, month, day, hour, min, sec; */ /* * Time ::= CHOICE { * utcTime UTCTime, * generalTime GeneralizedTime * } * * UTCTime: YYMMDDHHMMSSZ * GeneralizedTime: YYYYMMDDHHMMSSZ */ /* pos = (const char *) buf; switch (asn1_tag) { case ASN1_TAG_UTCTIME: if (len != 13 || buf[12] != 'Z') { return -1; } if (x509_atoi(pos, 2, &year) != 1) { return -1; } if (year < 50) year += 2000; else year += 1900; pos += 2; break; case ASN1_TAG_GENERALIZEDTIME: if (len != 15 || buf[14] != 'Z') { return -1; } if (x509_atoi(pos, 4, &year) != 1) { return -1; } pos += 4; break; default: return -1; } if (x509_atoi(pos, 2, &month) != 1) { return -1; } pos += 2; if (x509_atoi(pos, 2, &day) != 1) { return -1; } pos += 2; if (x509_atoi(pos, 2, &hour) != 1) { return -1; } pos += 2; if (x509_atoi(pos, 2, &min) != 1) { return -1; } pos += 2; if (x509_atoi(pos, 2, &sec) != 1) { return -1; } if (os_mktime(year, month, day, hour, min, sec, val) < 0) { if (year < 1970) { * At least some test certificates have been configured * to use dates prior to 1970. Set the date to * beginning of 1970 to handle these case. *val = 0; return 0; } return -1; } return 0; }*/ static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, struct x509_parser_time *val) { const char *pos = NULL; int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0; /* * Time ::= CHOICE { * utcTime UTCTime, * generalTime GeneralizedTime * } * * UTCTime: YYMMDDHHMMSSZ * GeneralizedTime: YYYYMMDDHHMMSSZ */ memset( val, 0, sizeof(*val) ); pos = (const char *) buf; switch (asn1_tag) { case ASN1_TAG_UTCTIME: if (len != 13 || buf[12] != 'Z') { return -1; } if (x509_atoi(pos, 2, &year) != 1) { return -1; } if (year < 50) year += 2000; else year += 1900; val->start = (unsigned char *)pos; pos += 2; break; case ASN1_TAG_GENERALIZEDTIME: if (len != 15 || buf[14] != 'Z') { return -1; } if (x509_atoi(pos, 4, &year) != 1) { return -1; } val->start = (unsigned char *)pos; pos += 4; break; default: return -1; } if (x509_atoi(pos, 2, &month) != 1) { return -1; } pos += 2; if (x509_atoi(pos, 2, &day) != 1) { return -1; } pos += 2; if (x509_atoi(pos, 2, &hour) != 1) { return -1; } pos += 2; if (x509_atoi(pos, 2, &min) != 1) { return -1; } pos += 2; if (x509_atoi(pos, 2, &sec) != 1) { return -1; } val->end = (unsigned char *)(pos + 2); val->year = (unsigned short) year; val->mon = (unsigned char) month; val->mday = (unsigned char) day; val->hour = (unsigned char) hour; val->min = (unsigned char) min; val->sec = (unsigned char) sec; return 0; } static int x509_parse_validity(const u8 *buf, size_t len, struct x509_certificate *cert, const u8 **next) { struct asn1_hdr hdr = {0}; const u8 *pos = NULL; size_t plen = 0; /* * Validity ::= SEQUENCE { * notBefore Time, * notAfter Time * } * * RFC 3280, 4.1.2.5: * CAs conforming to this profile MUST always encode certificate * validity dates through the year 2049 as UTCTime; certificate * validity dates in 2050 or later MUST be encoded as GeneralizedTime. */ if (asn1_get_next(buf, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { return -1; } pos = hdr.payload; plen = hdr.length; if (pos + plen > buf + len) return -1; *next = pos + plen; if (asn1_get_next(pos, plen, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || x509_parse_time(hdr.payload, hdr.length, hdr.tag, &cert->not_before) < 0) { return -1; } pos = hdr.payload + hdr.length; plen = *next - pos; if (asn1_get_next(pos, plen, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || x509_parse_time(hdr.payload, hdr.length, hdr.tag, &cert->not_after) < 0) { return -1; } return 0; } static int x509_id_ce_oid(struct asn1_oid *oid) { /* id-ce arc from X.509 for standard X.509v3 extensions */ return oid->len >= 4 && oid->oid[0] == 2 /* joint-iso-ccitt */ && oid->oid[1] == 5 /* ds */ && oid->oid[2] == 29 /* id-ce */; } #if 0 static int x509_id_pe_oid(struct asn1_oid *oid) { /* 1.3.6.1.5.5.7.1 - id-pe private X.509v3 extensions */ return oid->len >= 9 && oid->oid[0] == 1 /* ISO assigned OIDs */ && oid->oid[1] == 3 /* ISO Identified Organization */ && oid->oid[2] == 6 /* US Department of Defense */ && oid->oid[3] == 1 /* OID assignments from 1.3.6.1 - Internet */ && oid->oid[4] == 5 /* IANA Security-related objects */ && oid->oid[5] == 5 /* Mechanisms */ && oid->oid[6] == 7 /* PKIX */ && oid->oid[7] == 1 /* id-pe */ ; } #endif static int x509_parse_ext_key_usage(struct x509_certificate *cert, const u8 *pos, size_t len) { struct asn1_hdr hdr = {0}; /* * KeyUsage ::= BIT STRING { * digitalSignature (0), * nonRepudiation (1), * keyEncipherment (2), * dataEncipherment (3), * keyAgreement (4), * keyCertSign (5), * cRLSign (6), * encipherOnly (7), * decipherOnly (8) } */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_BITSTRING || hdr.length < 1) { return -1; } cert->exts.extensions_present |= X509_EXT_KEY_USAGE; cert->exts.key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length); return 0; } static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, const u8 *pos, size_t len) { struct asn1_hdr hdr = {0}; unsigned long value = 0; size_t left = 0; /* * BasicConstraints ::= SEQUENCE { * cA BOOLEAN DEFAULT FALSE, * pathLenConstraint INTEGER (0..MAX) OPTIONAL } */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { return -1; } cert->exts.extensions_present |= X509_EXT_BASIC_CONSTRAINTS; if (hdr.length == 0) return 0; if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL) { return -1; } if (hdr.tag == ASN1_TAG_BOOLEAN) { if (hdr.length != 1) { return -1; } cert->exts.ca = hdr.payload[0]; if (hdr.payload + hdr.length == pos + len) { return 0; } if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL) { return -1; } } if (hdr.tag != ASN1_TAG_INTEGER) { return -1; } pos = hdr.payload; left = hdr.length; value = 0; while (left) { value <<= 8; value |= *pos++; left--; } cert->exts.path_len_constraint = value; cert->exts.extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT; wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d " "pathLenConstraint=%lu", cert->exts.ca, cert->exts.path_len_constraint); return 0; } static int x509_parse_ext_subject_key_identifier(struct x509_certificate *cert, const u8 *pos, size_t len) { struct asn1_hdr hdr = {0}; /* SubjectKeyIdentifier ::= KeyIdentifier KeyIdentifier ::= OCTET STRING */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.tag != ASN1_TAG_OCTETSTRING) { return -1; } cert->exts.extensions_present |= X509_EXT_SUBJECT_KEY_IDENTIFIER; cert->exts.subjectKeyIdentifier.length = hdr.length; cert->exts.subjectKeyIdentifier.data = hdr.payload; wpa_printf(MSG_DEBUG, "X509: SubjectKeyIdentifier - length=%d ", cert->exts.subjectKeyIdentifier.length); return 0; } static int x509_parse_ext_authority_key_identifier(struct x509_certificate *cert, const u8 *pos, size_t len) { struct asn1_hdr hdr = {0}; /* AuthorityKeyIdentifier ::= SEQUENCE { keyIdentifier [0] KeyIdentifier OPTIONAL, authorityCertIssuer [1] GeneralNames OPTIONAL, ! NOT IMPLEMENTED authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL ! NOT IMPLEMENTED } KeyIdentifier ::= OCTET STRING */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { LOGD("x509_parse_ext_authority_key_identifier: not a sequence, tag=%d, class=%d", hdr.tag, hdr.class); return -1; } if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) { LOGD("x509_parse_ext_authority_key_identifier: not a expl_0"); return -1; } cert->exts.extensions_present |= X509_EXT_AUTHORITY_KEY_IDENTIFIER; cert->exts.authorityKeyIdentifier.length = hdr.length; cert->exts.authorityKeyIdentifier.data = hdr.payload; wpa_printf(MSG_DEBUG, "X509: AuthorityKeyIdentifier - length=%d ", cert->exts.authorityKeyIdentifier.length); return 0; } #if 0 static int x509_parse_ext_crl_distribution_points(struct x509_certificate *cert, const u8 *pos, size_t len) { struct asn1_hdr hdr; if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { return -1; } cert->exts.CRLDistributionPoints.length = len; cert->exts.CRLDistributionPoints.data = pos; cert->exts.extensions_present |= X509_EXT_CRL_DISTRIBUTION_POINTS; return 0; } static int x509_parse_ext_authority_info_access(struct x509_certificate *cert, const u8 *pos, size_t len) { struct asn1_hdr hdr; if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { return -1; } if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { return -1; } cert->exts.authorityInformationAccess.length = len; cert->exts.authorityInformationAccess.data = pos; cert->exts.extensions_present |= X509_EXT_AUTHORITY_INFORMATION_ACCESS; return 0; } #endif static int x509_parse_alt_name_rfc8222(struct x509_name *name, const u8 *pos, size_t len) { /* rfc822Name IA5String */ wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len); name->alt_email = (char const *) pos; name->alt_email_len = len; /* Check for broken string*/ if (os_strnlen(name->alt_email, len) != len) { wpa_printf(MSG_INFO, "X509: Reject certificate with " "embedded NUL byte in rfc822Name (%s[NUL])", name->alt_email); name->alt_email = NULL; name->alt_email_len = 0; return -1; } return 0; } static int x509_parse_alt_name_dns(struct x509_name *name, const u8 *pos, size_t len) { /* dNSName IA5String */ wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len); name->dns = (char const *) pos; name->dns_len = len; /* Check for broken string*/ if (os_strnlen(name->dns, len) != len) { wpa_printf(MSG_INFO, "X509: Reject certificate with " "embedded NUL byte in dNSName (%s[NUL])", name->dns); name->dns = NULL; name->dns_len = 0; return -1; } return 0; } static int x509_parse_alt_name_uri(struct x509_name *name, const u8 *pos, size_t len) { /* uniformResourceIdentifier IA5String */ wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - uniformResourceIdentifier", pos, len); name->uri = (char const *) pos; name->uri_len = len; /* Check for broken string*/ if (os_strnlen(name->uri, len) != len) { wpa_printf(MSG_INFO, "X509: Reject certificate with " "embedded NUL byte in uniformResourceIdentifier " "(%s[NUL])", name->uri); name->uri = NULL; name->uri_len = 0; return -1; } return 0; } static int x509_parse_alt_name_ip(struct x509_name *name, const u8 *pos, size_t len) { /* iPAddress OCTET STRING */ wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len); name->ip = pos; name->ip_len = len; return 0; } static int x509_parse_alt_name_rid(struct x509_name *name, const u8 *pos, size_t len) { char buf[80] = {0}; /* registeredID OBJECT IDENTIFIER */ if (asn1_parse_oid(pos, len, &name->rid) < 0) return -1; asn1_oid_to_str(&name->rid, buf, sizeof(buf)); wpa_printf(MSG_MSGDUMP, "X509: altName - registeredID: %s", buf); return 0; } static int x509_parse_ext_alt_name(struct x509_name *name, const u8 *pos, size_t len) { struct asn1_hdr hdr = {0}; const u8 *p = NULL, *end = NULL; /* * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName * * GeneralName ::= CHOICE { * otherName [0] OtherName, * rfc822Name [1] IA5String, * dNSName [2] IA5String, * x400Address [3] ORAddress, * directoryName [4] Name, * ediPartyName [5] EDIPartyName, * uniformResourceIdentifier [6] IA5String, * iPAddress [7] OCTET STRING, * registeredID [8] OBJECT IDENTIFIER } * * OtherName ::= SEQUENCE { * type-id OBJECT IDENTIFIER, * value [0] EXPLICIT ANY DEFINED BY type-id } * * EDIPartyName ::= SEQUENCE { * nameAssigner [0] DirectoryString OPTIONAL, * partyName [1] DirectoryString } */ for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) { int res; if (asn1_get_next(p, end - p, &hdr) < 0) { wpa_printf(MSG_DEBUG, "X509: Failed to parse SubjectAltName item"); return -1; } if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) continue; switch (hdr.tag) { case 1: res = x509_parse_alt_name_rfc8222(name, hdr.payload, hdr.length); break; case 2: res = x509_parse_alt_name_dns(name, hdr.payload, hdr.length); break; case 6: res = x509_parse_alt_name_uri(name, hdr.payload, hdr.length); break; case 7: res = x509_parse_alt_name_ip(name, hdr.payload, hdr.length); break; case 8: res = x509_parse_alt_name_rid(name, hdr.payload, hdr.length); break; case 0: /* TODO: otherName */ case 3: /* TODO: x500Address */ case 4: /* TODO: directoryName */ case 5: /* TODO: ediPartyName */ default: res = 0; break; } if (res < 0) return res; } return 0; } static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert, const u8 *pos, size_t len) { struct asn1_hdr hdr = {0}; /* SubjectAltName ::= GeneralNames */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " "SubjectAltName; found %d tag 0x%x", hdr.class, hdr.tag); return -1; } wpa_printf(MSG_DEBUG, "X509: SubjectAltName"); cert->exts.extensions_present |= X509_EXT_SUBJECT_ALT_NAME; if (hdr.length == 0) return 0; return x509_parse_ext_alt_name(&cert->subject, hdr.payload, hdr.length); } static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert, const u8 *pos, size_t len) { struct asn1_hdr hdr = {0}; /* IssuerAltName ::= GeneralNames */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " "IssuerAltName; found %d tag 0x%x", hdr.class, hdr.tag); return -1; } wpa_printf(MSG_DEBUG, "X509: IssuerAltName"); cert->exts.extensions_present |= X509_EXT_ISSUER_ALT_NAME; if (hdr.length == 0) return 0; return x509_parse_ext_alt_name(&cert->issuer, hdr.payload, hdr.length); } static int x509_parse_extension_data(struct x509_certificate *cert, struct asn1_oid *oid, const u8 *pos, size_t len, int critical_ext) { int id_ce = x509_id_ce_oid(oid); #if 0 int id_pe = x509_id_pe_oid(oid); #endif /* TODO: add other extensions required by RFC 3280, Ch 4.2: * certificate policies (section 4.2.1.5) * name constraints (section 4.2.1.11) * policy constraints (section 4.2.1.12) * extended key usage (section 4.2.1.13) * inhibit any-policy (section 4.2.1.15) */ if(id_ce) switch (oid->oid[3]) { case 14: /* id-ce-subjectKeyIdentifier */ return x509_parse_ext_subject_key_identifier(cert, pos, len); case 15: /* id-ce-keyUsage */ cert->exts.keyUsageCritical = critical_ext; return x509_parse_ext_key_usage(cert, pos, len); case 17: /* id-ce-subjectAltName */ return x509_parse_ext_subject_alt_name(cert, pos, len); case 18: /* id-ce-issuerAltName */ return x509_parse_ext_issuer_alt_name(cert, pos, len); case 19: /* id-ce-basicConstraints */ cert->exts.basicConstraintsCritical = critical_ext; return x509_parse_ext_basic_constraints(cert, pos, len); #if 0 case 31: /* id-ce-cRLDistributionPoints */ return x509_parse_ext_crl_distribution_points(cert, pos, len); #endif case 35: /* id-ce-authorityKeyIdentifier */ return x509_parse_ext_authority_key_identifier(cert, pos, len); default: return 1; } #if 0 if(id_pe) switch (oid->oid[8]) { case 1: /* id-pe-authorityInfoAccess */ return x509_parse_ext_authority_info_access(cert, pos, len); default: return 1; } #endif return 1; } static int x509_parse_extension(struct x509_certificate *cert, const u8 *pos, size_t len, const u8 **next) { const u8 *end = NULL; struct asn1_hdr hdr = {0}; struct asn1_oid oid; int critical_ext = 0, res = 0; char buf[80] = {0}; memset(&oid, 0, sizeof(oid)); /* * Extension ::= SEQUENCE { * extnID OBJECT IDENTIFIER, * critical BOOLEAN DEFAULT FALSE, * extnValue OCTET STRING * } */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " "Extensions: class %d tag 0x%x; expected SEQUENCE", hdr.class, hdr.tag); return -1; } pos = hdr.payload; *next = end = pos + hdr.length; if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) { wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for " "Extension (expected OID)"); return -1; } if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || (hdr.tag != ASN1_TAG_BOOLEAN && hdr.tag != ASN1_TAG_OCTETSTRING)) { wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " "Extensions: class %d tag 0x%x; expected BOOLEAN " "or OCTET STRING", hdr.class, hdr.tag); return -1; } if (hdr.tag == ASN1_TAG_BOOLEAN) { if (hdr.length != 1) { wpa_printf(MSG_DEBUG, "X509: Unexpected " "Boolean length (%u)", hdr.length); return -1; } critical_ext = hdr.payload[0]; pos = hdr.payload; if (asn1_get_next(pos, end - pos, &hdr) < 0 || (hdr.class != ASN1_CLASS_UNIVERSAL && hdr.class != ASN1_CLASS_PRIVATE) || hdr.tag != ASN1_TAG_OCTETSTRING) { wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header " "in Extensions: class %d tag 0x%x; " "expected OCTET STRING", hdr.class, hdr.tag); return -1; } } asn1_oid_to_str(&oid, buf, sizeof(buf)); LOGD("X509: Extension: extnID=%s critical=%d", buf, critical_ext); wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length); res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length, critical_ext); if (res < 0) return res; if (res == 1 && critical_ext) { LOGE("X509: Unknown critical extension %s", buf); return -1; } return 0; } static int x509_parse_extensions(struct x509_certificate *cert, const u8 *pos, size_t len) { const u8 *end = NULL; struct asn1_hdr hdr = {0}; /* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data " "for Extensions: class %d tag 0x%x; " "expected SEQUENCE", hdr.class, hdr.tag); return -1; } pos = hdr.payload; end = pos + hdr.length; while (pos < end) { if (x509_parse_extension(cert, pos, end - pos, &pos) < 0) return -1; } return 0; } static int x509_parse_tbs_certificate(const u8 *buf, size_t len, struct x509_certificate *cert, const u8 **next) { struct asn1_hdr hdr = {0}; const u8 *pos = NULL, *end = NULL; size_t left = 0; /*char sbuf[128];*/ unsigned long value = 0; /* tbsCertificate TBSCertificate ::= SEQUENCE */ if (asn1_get_next(buf, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start " "with a valid SEQUENCE - found class %d tag 0x%x", hdr.class, hdr.tag); return X509_PARSE_ERROR_INVALID_SEQUENCE; } pos = hdr.payload; end = *next = pos + hdr.length; /* * version [0] EXPLICIT Version DEFAULT v1 * Version ::= INTEGER { v1(0), v2(1), v3(2) } */ if (asn1_get_next(pos, end - pos, &hdr) < 0) return X509_PARSE_ERROR_INVALID_HEADER; pos = hdr.payload; if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) { if (asn1_get_next(pos, end - pos, &hdr) < 0) return X509_PARSE_ERROR_INVALID_HEADER; if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " "version field - found class %d tag 0x%x", hdr.class, hdr.tag); return X509_PARSE_ERROR_VERSION_TAG; } if (hdr.length != 1) { wpa_printf(MSG_DEBUG, "X509: Unexpected version field " "length %u (expected 1)", hdr.length); return X509_PARSE_ERROR_VERSION_TAG; } pos = hdr.payload; left = hdr.length; value = 0; while (left) { value <<= 8; value |= *pos++; left--; } cert->version = (cert_version_t)value; if (cert->version != X509_CERT_V1 && cert->version != X509_CERT_V2 && cert->version != X509_CERT_V3) { wpa_printf(MSG_DEBUG, "X509: Unsupported version %d", cert->version + 1); return X509_PARSE_ERROR_VERSION_TAG; } if (asn1_get_next(pos, end - pos, &hdr) < 0) return X509_PARSE_ERROR_INVALID_HEADER; } else cert->version = X509_CERT_V1; wpa_printf(MSG_MSGDUMP, "X509: Version X.509v%d", cert->version + 1); /* serialNumber CertificateSerialNumber ::= INTEGER */ if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " "serialNumber; class=%d tag=0x%x", hdr.class, hdr.tag); return X509_PARSE_ERROR_INVALID_TAG; } pos = hdr.payload; left = hdr.length; while (left) { cert->serial_number <<= 8; cert->serial_number |= *pos++; left--; } wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu(0x%lx)", cert->serial_number, cert->serial_number); /* signature AlgorithmIdentifier */ if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature, &pos)) return X509_PARSE_ERROR_ALGORITHM_IDENTIFIER; /* issuer Name */ if (x509_parse_name(pos, end - pos, &cert->issuer, &pos)) return X509_PARSE_ERROR_NAME; /*x509_name_string(&cert->issuer, sbuf, sizeof(sbuf)); wpa_printf(MSG_MSGDUMP, "X509: issuer %s", sbuf);*/ /* validity Validity */ if (x509_parse_validity(pos, end - pos, cert, &pos)) return X509_PARSE_ERROR_VALIDITY; /* subject Name */ if (x509_parse_name(pos, end - pos, &cert->subject, &pos)) return X509_PARSE_ERROR_NAME; /*x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf);*/ /* subjectPublicKeyInfo SubjectPublicKeyInfo */ cert->subject_public_key = pos; if (x509_parse_public_key(pos, end - pos, cert, &pos)) return X509_PARSE_ERROR_PUBLIC_KEY; cert->subject_public_key_len = pos - cert->subject_public_key; if (pos == end) return 0; if (cert->version == X509_CERT_V1) return 0; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" " tag to parse optional tbsCertificate " "field(s); parsed class %d tag 0x%x", hdr.class, hdr.tag); return X509_PARSE_ERROR_INVALID_HEADER; } if (hdr.tag == 1) { /* issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL */ wpa_printf(MSG_DEBUG, "X509: issuerUniqueID"); /* TODO: parse UniqueIdentifier ::= BIT STRING */ if (hdr.payload + hdr.length == end) return 0; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" " tag to parse optional tbsCertificate " "field(s); parsed class %d tag 0x%x", hdr.class, hdr.tag); return X509_PARSE_ERROR_INVALID_TAG; } } if (hdr.tag == 2) { /* subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL */ wpa_printf(MSG_DEBUG, "X509: subjectUniqueID"); /* TODO: parse UniqueIdentifier ::= BIT STRING */ if (hdr.payload + hdr.length == end) return 0; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" " tag to parse optional tbsCertificate " "field(s); parsed class %d tag 0x%x", hdr.class, hdr.tag); return X509_PARSE_ERROR_INVALID_HEADER; } } if (hdr.tag != 3) { wpa_printf(MSG_DEBUG, "X509: Ignored unexpected " "Context-Specific tag %d in optional " "tbsCertificate fields", hdr.tag); return 0; } /* extensions [3] EXPLICIT Extensions OPTIONAL */ if (cert->version != X509_CERT_V3) { wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and " "Extensions data which are only allowed for " "version 3", cert->version + 1); return X509_PARSE_ERROR_EXTENSIONS; } if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0) return X509_PARSE_ERROR_EXTENSIONS; pos = hdr.payload + hdr.length; if (pos < end) { wpa_hexdump(MSG_DEBUG, "X509: Ignored extra tbsCertificate data", pos, end - pos); } return 0; } #if 0 static int x509_rsadsi_oid(struct asn1_oid *oid) { return oid->len >= 4 && oid->oid[0] == 1 /* iso */ && oid->oid[1] == 2 /* member-body */ && oid->oid[2] == 840 /* us */ && oid->oid[3] == 113549 /* rsadsi */; } static int x509_pkcs_oid(struct asn1_oid *oid) { return oid->len >= 5 && x509_rsadsi_oid(oid) && oid->oid[4] == 1 /* pkcs */; } static int x509_digest_oid(struct asn1_oid *oid) { return oid->len >= 5 && x509_rsadsi_oid(oid) && oid->oid[4] == 2 /* digestAlgorithm */; } static int x509_sha1_oid(struct asn1_oid *oid) { return oid->len == 6 && oid->oid[0] == 1 /* iso */ && oid->oid[1] == 3 /* identified-organization */ && oid->oid[2] == 14 /* oiw */ && oid->oid[3] == 3 /* secsig */ && oid->oid[4] == 2 /* algorithms */ && oid->oid[5] == 26 /* id-sha1 */; } static int x509_sha256_oid(struct asn1_oid *oid) { return oid->len == 9 && oid->oid[0] == 2 /* joint-iso-itu-t */ && oid->oid[1] == 16 /* country */ && oid->oid[2] == 840 /* us */ && oid->oid[3] == 1 /* organization */ && oid->oid[4] == 101 /* gov */ && oid->oid[5] == 3 /* csor */ && oid->oid[6] == 4 /* nistAlgorithm */ && oid->oid[7] == 2 /* hashAlgs */ && oid->oid[8] == 1 /* sha256 */; } static int x509_valid_issuer(const struct x509_certificate *cert) { if ((cert->exts.extensions_present & X509_EXT_BASIC_CONSTRAINTS) && !cert->exts.ca) { wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an " "issuer"); return -1; } if (cert->version == X509_CERT_V3 && !(cert->exts.extensions_present & X509_EXT_BASIC_CONSTRAINTS)) { wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not " "include BasicConstraints extension"); return -1; } if ((cert->exts.extensions_present & X509_EXT_KEY_USAGE) && !(cert->exts.key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) { wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have " "keyCertSign bit in Key Usage"); return -1; } return 0; } #endif /** * x509_certificate_parse - Parse a X.509 certificate in DER format * @buf: Pointer to the X.509 certificate in DER format * @len: Buffer length * @px509_certificate pointer to filled certificate * Returns: x509_parser_error_t Error Code * */ x509_parser_error_t x509_certificate_parse(const u8 *buf, size_t len, struct x509_certificate * px509_certificate) { struct asn1_hdr hdr = {0}; const u8 *pos = NULL, *end = NULL, *hash_start = NULL; x509_parser_error_t ParserError = X509_PARSE_ERROR_UNDEFINED; struct x509_certificate *cert = px509_certificate; if( NULL == buf || 0 == len || NULL == px509_certificate ) { wpa_printf(MSG_ERROR, "X509: Invalid input parameters buf=%p, len=%u", buf, len); return X509_PARSE_ERROR_INVALID_ARGUMENTS; } /* initialize certificate buffer with zero values, non parsed certificate */ os_memset(cert, 0, sizeof(*cert)); cert->cert_start = buf; cert->cert_len = len; pos = buf; end = buf + len; /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */ /* Certificate ::= SEQUENCE */ if (asn1_get_next(pos, len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Certificate did not start with " "a valid SEQUENCE - found class %d tag 0x%x", hdr.class, hdr.tag); return X509_PARSE_ERROR_INVALID_SEQUENCE; } cert->cert_len = hdr.length + (hdr.payload - buf); pos = hdr.payload; if (pos + hdr.length > end) { return X509_PARSE_ERROR_INVALID_HEADER; } if (pos + hdr.length < end) { wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " "encoded certificate", pos + hdr.length, end - pos + hdr.length); end = pos + hdr.length; } hash_start = pos; cert->tbs_cert_start = cert->cert_start + (hash_start - buf); ParserError = (x509_parser_error_t)x509_parse_tbs_certificate(pos, end - pos, cert, &pos); if ( ParserError ) { return ParserError; } cert->tbs_cert_len = pos - hash_start; /* signatureAlgorithm AlgorithmIdentifier */ ParserError = (x509_parser_error_t)x509_parse_algorithm_identifier(pos, end - pos, &cert->signature_alg, &pos); if ( ParserError ) { LOGE("X509 : %d", ParserError); return X509_PARSE_ERROR_ALGORITHM_IDENTIFIER; } /* signatureValue BIT STRING */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_BITSTRING) { wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " "(signatureValue) - found class %d tag 0x%x", hdr.class, hdr.tag); return X509_PARSE_ERROR_EXPECTED_BITSTRING; } if (hdr.length < 1) { return X509_PARSE_ERROR_INVALID_HEADER; } pos = hdr.payload; if (*pos) { wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", *pos); /* PKCS #1 v1.5 10.2.1: * It is an error if the length in bits of the signature S is * not a multiple of eight. */ return X509_PARSE_ERROR_SIGNATURE_LENGTH; } cert->sign_value = pos + 1; cert->sign_value_len = hdr.length - 1; wpa_hexdump(MSG_MSGDUMP, "X509: signature", cert->sign_value, cert->sign_value_len); return X509_PARSE_OK; } /** * x509_certificate_check_signature - Verify certificate signature * @issuer: Issuer certificate * @cert: Certificate to be verified * Returns: 0 if cert has a valid signature that was signed by the issuer, * -1 if not */ /*int x509_certificate_check_signature(struct x509_certificate *issuer, struct x509_certificate *cert) { struct crypto_public_key *pk; u8 *data; const u8 *pos, *end, *next, *da_end; size_t data_len; struct asn1_hdr hdr; struct asn1_oid oid; u8 hash[32]; size_t hash_len; if (!x509_pkcs_oid(&cert->signature.oid) || cert->signature.oid.len != 7 || cert->signature.oid.oid[5] != 1 { wpa_printf(MSG_DEBUG, "X509: Unrecognized signature " "algorithm"); return -1; } pk = crypto_public_key_import(issuer->public_key, issuer->public_key_len); if (pk == NULL) return -1; data_len = cert->sign_value_len; data = os_malloc(data_len); if (data == NULL) { crypto_public_key_free(pk); return -1; } if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value, cert->sign_value_len, data, &data_len) < 0) { wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature"); crypto_public_key_free(pk); os_free(data); return -1; } crypto_public_key_free(pk); wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len); if (asn1_get_next(data, data_len, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " "(DigestInfo) - found class %d tag 0x%x", hdr.class, hdr.tag); os_free(data); return -1; } pos = hdr.payload; end = pos + hdr.length; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SEQUENCE) { wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " "(AlgorithmIdentifier) - found class %d tag 0x%x", hdr.class, hdr.tag); os_free(data); return -1; } da_end = hdr.payload + hdr.length; if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) { wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm"); os_free(data); return -1; } if (x509_sha1_oid(&oid)) { if (cert->signature.oid.oid[6] != 5 ) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 " "does not match with certificate " "signatureAlgorithm (%lu)", cert->signature.oid.oid[6]); os_free(data); return -1; } goto skip_digest_oid; } if (x509_sha256_oid(&oid)) { if (cert->signature.oid.oid[6] != 11 ) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 " "does not match with certificate " "signatureAlgorithm (%lu)", cert->signature.oid.oid[6]); os_free(data); return -1; } goto skip_digest_oid; } if (!x509_digest_oid(&oid)) { wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm"); os_free(data); return -1; } switch (oid.oid[5]) { case 5: if (cert->signature.oid.oid[6] != 4 ) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does " "not match with certificate " "signatureAlgorithm (%lu)", cert->signature.oid.oid[6]); os_free(data); return -1; } break; case 2: case 4: default: wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm " "(%lu)", oid.oid[5]); os_free(data); return -1; } skip_digest_oid: pos = da_end; end = data + data_len; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OCTETSTRING) { wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING " "(Digest) - found class %d tag 0x%x", hdr.class, hdr.tag); os_free(data); return -1; } wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest", hdr.payload, hdr.length); switch (cert->signature.oid.oid[6]) { case 4: md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, hash); hash_len = 16; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)", hash, hash_len); break; case 5: sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, hash); hash_len = 20; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)", hash, hash_len); break; case 11: sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, hash); hash_len = 32; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)", hash, hash_len); break; case 2: case 12: case 13: default: wpa_printf(MSG_INFO, "X509: Unsupported certificate signature " "algorithm (%lu)", cert->signature.oid.oid[6]); os_free(data); return -1; } if (hdr.length != hash_len || os_memcmp(hdr.payload, hash, hdr.length) != 0) { wpa_printf(MSG_INFO, "X509: Certificate Digest does not match " "with calculated tbsCertificate hash"); os_free(data); return -1; } os_free(data); wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with " "calculated tbsCertificate hash"); return 0; } */ /** * x509_certificate_get_subject - Get a certificate based on Subject name * @chain: Certificate chain to search through * @name: Subject name to search for * Returns: Pointer to the certificate with the given Subject name or * %NULL on failure */ struct x509_certificate * x509_certificate_get_by_subject(struct x509_certificate *chain, struct x509_name *name) { struct x509_certificate *cert = NULL; for (cert = chain; cert; cert = cert->next) { if (x509_name_compare(&cert->subject, name) == 0) return cert; } return NULL; } /** * x509_certificate_self_signed - Is the certificate self-signed? * @cert: Certificate * Returns: 1 if certificate is self-signed, 0 if not */ int x509_certificate_self_signed(struct x509_certificate *cert) { return x509_name_compare(&cert->issuer, &cert->subject) == 0; }
Editor is loading...
Leave a Comment