static int asn1_find_oid(const uint8_t* cert, int* offset, const uint8_t* oid, int oid_length) { int seqlen; if ((seqlen = asn1_next_obj(cert, offset, ASN1_SEQUENCE))> 0) { int end = *offset + seqlen; while (*offset < end) { int type = cert[(*offset)++]; int length = get_asn1_length(cert, offset); int noffset = *offset + length; if (type == ASN1_SEQUENCE) { type = cert[(*offset)++]; length = get_asn1_length(cert, offset); if (type == ASN1_OID && length == oid_length && memcmp(cert + *offset, oid, oid_length) == 0) { *offset += oid_length; return 1; } } *offset = noffset; } } return 0; }
/** * Skip the ASN1.1 object type and its length. Get ready to read the object's * data. */ int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type) { if (buf[*offset] != obj_type) return X509_NOT_OK; (*offset)++; return get_asn1_length(buf, offset); }
/** * Read the signature type of the certificate. We only support RSA-MD5 and * RSA-SHA1 signature types. */ int asn1_signature_type(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) { int ret = X509_NOT_OK, len; if (cert[(*offset)++] != ASN1_OID) goto end_check_sig; len = get_asn1_length(cert, offset); if (len == 5 && memcmp(sig_iis6_oid, &cert[*offset], SIG_IIS6_OID_SIZE) == 0) { x509_ctx->sig_type = SIG_TYPE_SHA1; } else { if (memcmp(sig_oid_prefix, &cert[*offset], SIG_OID_PREFIX_SIZE)) goto end_check_sig; /* unrecognised cert type */ x509_ctx->sig_type = cert[*offset + SIG_OID_PREFIX_SIZE]; } *offset += len; asn1_skip_obj(cert, offset, ASN1_NULL); /* if it's there */ ret = X509_OK; end_check_sig: return ret; }
/** * Get the time of a certificate. Ignore hours/minutes/seconds. */ static int asn1_get_utc_time(const uint8_t *buf, int *offset, time_t *t) { int ret = X509_NOT_OK, len, t_offset; struct tm tm; if (buf[(*offset)++] != ASN1_UTC_TIME) goto end_utc_time; len = get_asn1_length(buf, offset); t_offset = *offset; memset(&tm, 0, sizeof(struct tm)); tm.tm_year = (buf[t_offset] - '0')*10 + (buf[t_offset+1] - '0'); if (tm.tm_year <= 50) /* 1951-2050 thing */ { tm.tm_year += 100; } tm.tm_mon = (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0') - 1; tm.tm_mday = (buf[t_offset+4] - '0')*10 + (buf[t_offset+5] - '0'); *t = mktime(&tm); *offset += len; ret = X509_OK; end_utc_time: return ret; }
/** * Read the signature type of the certificate. We only support RSA-MD5 and * RSA-SHA1 signature types. */ int asn1_signature_type(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) { int ret = X509_NOT_OK, len; if (cert[(*offset)++] != ASN1_OID) goto end_check_sig; len = get_asn1_length(cert, offset); if (len == sizeof(sig_sha1WithRSAEncrypt) && memcmp(sig_sha1WithRSAEncrypt, &cert[*offset], sizeof(sig_sha1WithRSAEncrypt)) == 0) { x509_ctx->sig_type = SIG_TYPE_SHA1; } else if (len == sizeof(sig_sha256) && memcmp(sig_sha256, &cert[*offset], sizeof(sig_sha256)) == 0) { x509_ctx->sig_type = SIG_TYPE_SHA256; } else if (len == sizeof(sig_sha384) && memcmp(sig_sha384, &cert[*offset], sizeof(sig_sha384)) == 0) { x509_ctx->sig_type = SIG_TYPE_SHA384; } else if (len == sizeof(sig_sha512) && memcmp(sig_sha512, &cert[*offset], sizeof(sig_sha512)) == 0) { x509_ctx->sig_type = SIG_TYPE_SHA512; } else { if (memcmp(sig_oid_prefix, &cert[*offset], sizeof(sig_oid_prefix))) { #ifdef CONFIG_SSL_FULL_MODE int i; printf("invalid digest: "); for (i = 0; i < len; i++) printf("%02x ", cert[*offset + i]); printf("\n"); #endif goto end_check_sig; /* unrecognised cert type */ } x509_ctx->sig_type = cert[*offset + sizeof(sig_oid_prefix)]; } *offset += len; asn1_skip_obj(cert, offset, ASN1_NULL); /* if it's there */ ret = X509_OK; end_check_sig: return ret; }
/** * Skip over an ASN.1 object type completely. Get ready to read the next * object. */ int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type) { int len; if (buf[*offset] != obj_type) return X509_NOT_OK; (*offset)++; len = get_asn1_length(buf, offset); *offset += len; return 0; }
/** * Read the signature type of the certificate. We only support RSA-MD5 and * RSA-SHA1 signature types. */ int ICACHE_FLASH_ATTR asn1_signature_type(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) { int ret = X509_NOT_OK, len; if (cert[(*offset)++] != ASN1_OID) goto end_check_sig; len = get_asn1_length(cert, offset); if (len == sizeof(sig_sha1WithRSAEncrypt) && memcmp(sig_sha1WithRSAEncrypt, &cert[*offset], sizeof(sig_sha1WithRSAEncrypt)) == 0) { x509_ctx->sig_type = SIG_TYPE_SHA1; } else if (len == sizeof(sig_sha256) && memcmp(sig_sha256, &cert[*offset], sizeof(sig_sha256)) == 0) { x509_ctx->sig_type = SIG_TYPE_SHA256; } else if (len == sizeof(sig_sha384) && memcmp(sig_sha384, &cert[*offset], sizeof(sig_sha384)) == 0) { x509_ctx->sig_type = SIG_TYPE_SHA384; } else if (len == sizeof(sig_sha512) && memcmp(sig_sha512, &cert[*offset], sizeof(sig_sha512)) == 0) { x509_ctx->sig_type = SIG_TYPE_SHA512; } else { if (memcmp(sig_oid_prefix, &cert[*offset], sizeof(sig_oid_prefix))) goto end_check_sig; /* unrecognised cert type */ x509_ctx->sig_type = cert[*offset + sizeof(sig_oid_prefix)]; } *offset += len; asn1_skip_obj(cert, offset, ASN1_NULL); /* if it's there */ ret = X509_OK; end_check_sig: return ret; }
/** * Read the signature of the certificate. */ int asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) { int ret = X509_NOT_OK; if (cert[(*offset)++] != ASN1_BIT_STRING) goto end_sig; x509_ctx->sig_len = get_asn1_length(cert, offset)-1; (*offset)++; /* ignore bit string padding bits */ x509_ctx->signature = (uint8_t *)malloc(x509_ctx->sig_len); memcpy(x509_ctx->signature, &cert[*offset], x509_ctx->sig_len); *offset += x509_ctx->sig_len; ret = X509_OK; end_sig: return ret; }
/** * Retrieve the signature from a certificate. */ static const uint8_t *get_signature(const uint8_t *asn1_sig, int *len) { int offset = 0; const uint8_t *ptr = NULL; if (asn1_next_obj(asn1_sig, &offset, ASN1_SEQUENCE) < 0 || asn1_skip_obj(asn1_sig, &offset, ASN1_SEQUENCE)) goto end_get_sig; if (asn1_sig[offset++] != ASN1_OCTET_STRING) goto end_get_sig; *len = get_asn1_length(asn1_sig, &offset); ptr = &asn1_sig[offset]; /* all ok */ end_get_sig: return ptr; }
/* Saves a few bytes of memory */ bi_clear_cache(bi_ctx); #endif ret = X509_OK; end_cert: if (len) { *len = cert_size; } if (ret) { #ifdef CONFIG_SSL_FULL_MODE char buff[64]; printf("Error: Invalid X509 ASN.1 file (%s)\n", x509_display_error(ret, buff)); #endif x509_free(x509_ctx); *ctx = NULL; } return ret; } #ifdef CONFIG_SSL_CERT_VERIFICATION /* only care if doing verification */ static int x509_v3_subject_alt_name(const uint8_t *cert, int offset, X509_CTX *x509_ctx) { if ((offset = asn1_is_subject_alt_name(cert, offset)) > 0) { x509_ctx->subject_alt_name_present = true; x509_ctx->subject_alt_name_is_critical = asn1_is_critical_ext(cert, &offset); if (asn1_next_obj(cert, &offset, ASN1_OCTET_STRING) > 0) { int altlen; if ((altlen = asn1_next_obj(cert, &offset, ASN1_SEQUENCE)) > 0) { int endalt = offset + altlen; int totalnames = 0; while (offset < endalt) { int type = cert[offset++]; int dnslen = get_asn1_length(cert, &offset); if (type == ASN1_CONTEXT_DNSNAME) { x509_ctx->subject_alt_dnsnames = (char**) realloc(x509_ctx->subject_alt_dnsnames, (totalnames + 2) * sizeof(char*)); x509_ctx->subject_alt_dnsnames[totalnames] = (char*)malloc(dnslen + 1); x509_ctx->subject_alt_dnsnames[totalnames+1] = NULL; memcpy(x509_ctx->subject_alt_dnsnames[totalnames], cert + offset, dnslen); x509_ctx->subject_alt_dnsnames[totalnames][dnslen] = 0; totalnames++; } offset += dnslen; } } } } return X509_OK; }
/** * Obtain an ASN.1 printable string type. */ static int asn1_get_printable_str(const uint8_t *buf, int *offset, char **str) { int len = X509_NOT_OK; int string_type = buf[*offset]; /* GBG */ /* some certs have this awful crud in them for some reason */ if (string_type != ASN1_PRINTABLE_STR && string_type != ASN1_TELETEX_STR && string_type != ASN1_IA5_STR && string_type != ASN1_UNICODE_STR && string_type != ASN1_UTF8_STR && string_type != ASN1_UNIVERSAL_STR) goto end_pnt_str; (*offset)++; len = get_asn1_length(buf, offset); if (string_type == ASN1_UNICODE_STR) { int i; *str = (char *)malloc(len/2+1); /* allow for null */ for (i = 0; i < len; i += 2) (*str)[i/2] = buf[*offset + i + 1]; (*str)[len/2] = 0; /* null terminate */ } else { *str = (char *)malloc(len+1); /* allow for null */ memcpy(*str, &buf[*offset], len); (*str)[len] = 0; /* null terminate */ } *offset += len; end_pnt_str: return len; }
/** * Get the time of a certificate. Ignore hours/minutes/seconds. */ static int asn1_get_utc_time(const uint8_t *buf, int *offset, SSL_DateTime *t) { int ret = X509_NOT_OK, len, t_offset; uint8_t time_encoding; memset(t, 0, sizeof(*t)); time_encoding = buf[(*offset)++]; if (time_encoding != ASN1_UTC_TIME && time_encoding != ASN1_GENERALIZED_TIME) /* GBG */ goto end_utc_time; len = get_asn1_length(buf, offset); t_offset = *offset; if (time_encoding == ASN1_UTC_TIME) { t->year = (buf[t_offset] - '0')*10 + (buf[t_offset+1] - '0'); if (t->year <= 50) /* 1951-2050 thing */ { t->year += 100; } t->year += 1900; t_offset += 2; } else { t->year = (buf[t_offset ] - '0')*1000 + (buf[t_offset+1] - '0')*100 + (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0'); t_offset += 4; } t->month = (buf[t_offset ] - '0')*10 + (buf[t_offset+1] - '0'); t->day = (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0'); *offset += len; ret = X509_OK; end_utc_time: return ret; }
/** * Get the time of a certificate. Ignore hours/minutes/seconds. */ static int asn1_get_utc_time(const uint8_t *buf, int *offset, time_t *t) { int ret = X509_NOT_OK, len, t_offset, abs_year; struct tm tm; /* see http://tools.ietf.org/html/rfc5280#section-4.1.2.5 */ if (buf[*offset] == ASN1_UTC_TIME) { (*offset)++; len = get_asn1_length(buf, offset); t_offset = *offset; memset(&tm, 0, sizeof(struct tm)); tm.tm_year = (buf[t_offset] - '0')*10 + (buf[t_offset+1] - '0'); if (tm.tm_year <= 50) /* 1951-2050 thing */ { tm.tm_year += 100; } tm.tm_mon = (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0') - 1; tm.tm_mday = (buf[t_offset+4] - '0')*10 + (buf[t_offset+5] - '0'); *t = mktime(&tm); *offset += len; ret = X509_OK; } else if (buf[*offset] == ASN1_GENERALIZED_TIME) { (*offset)++; len = get_asn1_length(buf, offset); t_offset = *offset; memset(&tm, 0, sizeof(struct tm)); abs_year = ((buf[t_offset] - '0')*1000 + (buf[t_offset+1] - '0')*100 + (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0')); if (abs_year <= 1901) { tm.tm_year = 1; tm.tm_mon = 0; tm.tm_mday = 1; } else { tm.tm_year = abs_year - 1900; tm.tm_mon = (buf[t_offset+4] - '0')*10 + (buf[t_offset+5] - '0') - 1; tm.tm_mday = (buf[t_offset+6] - '0')*10 + (buf[t_offset+7] - '0'); tm.tm_hour = (buf[t_offset+8] - '0')*10 + (buf[t_offset+9] - '0'); tm.tm_min = (buf[t_offset+10] - '0')*10 + (buf[t_offset+11] - '0'); tm.tm_sec = (buf[t_offset+12] - '0')*10 + (buf[t_offset+13] - '0'); *t = mktime(&tm); } *offset += len; ret = X509_OK; } return ret; }
/** * Construct a new x509 object. * @return 0 if ok. < 0 if there was a problem. */ int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx) { int begin_tbs, end_tbs; int ret = X509_NOT_OK, offset = 0, cert_size = 0; X509_CTX *x509_ctx; BI_CTX *bi_ctx; *ctx = (X509_CTX *)calloc(1, sizeof(X509_CTX)); x509_ctx = *ctx; /* get the certificate size */ asn1_skip_obj(cert, &cert_size, ASN1_SEQUENCE); if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) goto end_cert; begin_tbs = offset; /* start of the tbs */ end_tbs = begin_tbs; /* work out the end of the tbs */ asn1_skip_obj(cert, &end_tbs, ASN1_SEQUENCE); if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) goto end_cert; if (cert[offset] == ASN1_EXPLICIT_TAG) /* optional version */ { if (asn1_version(cert, &offset, x509_ctx)) goto end_cert; } if (asn1_skip_obj(cert, &offset, ASN1_INTEGER) || /* serial number */ asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) goto end_cert; /* make sure the signature is ok */ if (asn1_signature_type(cert, &offset, x509_ctx)) { ret = X509_VFY_ERROR_UNSUPPORTED_DIGEST; goto end_cert; } if (asn1_name(cert, &offset, x509_ctx->ca_cert_dn) || asn1_validity(cert, &offset, x509_ctx) || asn1_name(cert, &offset, x509_ctx->cert_dn) || asn1_public_key(cert, &offset, x509_ctx)) { goto end_cert; } bi_ctx = x509_ctx->rsa_ctx->bi_ctx; x509_ctx->fingerprint = malloc(SHA1_SIZE); SHA1_CTX sha_fp_ctx; SHA1_Init(&sha_fp_ctx); SHA1_Update(&sha_fp_ctx, &cert[0], cert_size); SHA1_Final(x509_ctx->fingerprint, &sha_fp_ctx); #ifdef CONFIG_SSL_CERT_VERIFICATION /* only care if doing verification */ /* use the appropriate signature algorithm (SHA1/MD5/MD2) */ if (x509_ctx->sig_type == SIG_TYPE_MD5) { MD5_CTX md5_ctx; uint8_t md5_dgst[MD5_SIZE]; MD5_Init(&md5_ctx); MD5_Update(&md5_ctx, &cert[begin_tbs], end_tbs-begin_tbs); MD5_Final(md5_dgst, &md5_ctx); x509_ctx->digest = bi_import(bi_ctx, md5_dgst, MD5_SIZE); } else if (x509_ctx->sig_type == SIG_TYPE_SHA1) { SHA1_CTX sha_ctx; uint8_t sha_dgst[SHA1_SIZE]; SHA1_Init(&sha_ctx); SHA1_Update(&sha_ctx, &cert[begin_tbs], end_tbs-begin_tbs); SHA1_Final(sha_dgst, &sha_ctx); x509_ctx->digest = bi_import(bi_ctx, sha_dgst, SHA1_SIZE); } else if (x509_ctx->sig_type == SIG_TYPE_MD2) { MD2_CTX md2_ctx; uint8_t md2_dgst[MD2_SIZE]; MD2_Init(&md2_ctx); MD2_Update(&md2_ctx, &cert[begin_tbs], end_tbs-begin_tbs); MD2_Final(md2_dgst, &md2_ctx); x509_ctx->digest = bi_import(bi_ctx, md2_dgst, MD2_SIZE); } if (cert[offset] == ASN1_V3_DATA) { int suboffset; ++offset; get_asn1_length(cert, &offset); if ((suboffset = asn1_find_subjectaltname(cert, offset)) > 0) { if (asn1_next_obj(cert, &suboffset, ASN1_OCTET_STRING) > 0) { int altlen; if ((altlen = asn1_next_obj(cert, &suboffset, ASN1_SEQUENCE)) > 0) { int endalt = suboffset + altlen; int totalnames = 0; while (suboffset < endalt) { int type = cert[suboffset++]; int dnslen = get_asn1_length(cert, &suboffset); if (type == ASN1_CONTEXT_DNSNAME) { x509_ctx->subject_alt_dnsnames = (char**) realloc(x509_ctx->subject_alt_dnsnames, (totalnames + 2) * sizeof(char*)); x509_ctx->subject_alt_dnsnames[totalnames] = (char*)malloc(dnslen + 1); x509_ctx->subject_alt_dnsnames[totalnames+1] = NULL; memcpy(x509_ctx->subject_alt_dnsnames[totalnames], cert + suboffset, dnslen); x509_ctx->subject_alt_dnsnames[ totalnames][dnslen] = 0; ++totalnames; } suboffset += dnslen; } } } } } offset = end_tbs; /* skip the rest of v3 data */ if (asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) || asn1_signature(cert, &offset, x509_ctx)) goto end_cert; #endif ret = X509_OK; end_cert: if (len) { *len = cert_size; } if (ret) { #ifdef CONFIG_SSL_FULL_MODE printf("Error: Invalid X509 ASN.1 file (%s)\n", x509_display_error(ret)); #endif x509_free(x509_ctx); *ctx = NULL; } return ret; }