diff --git a/include/mbedtls/x509_crt.h b/include/mbedtls/x509_crt.h index e1b4aa238..57e3cce1a 100644 --- a/include/mbedtls/x509_crt.h +++ b/include/mbedtls/x509_crt.h @@ -241,6 +241,9 @@ typedef struct mbedtls_x509write_cert { } mbedtls_x509write_cert; +int mbedtls_x509write_crt_set_subject_alternative_name(mbedtls_x509write_cert *ctx, + const mbedtls_x509_san_list *san_list); + /** * Item in a verification chain: cert and flags for it */ diff --git a/library/x509write_crt.c b/library/x509write_crt.c index 70d7e93db..bcc9cb007 100644 --- a/library/x509write_crt.c +++ b/library/x509write_crt.c @@ -31,6 +31,7 @@ #include "mbedtls/asn1write.h" #include "mbedtls/error.h" #include "mbedtls/oid.h" +#include "mbedtls/platform.h" #include "mbedtls/platform_util.h" #include "mbedtls/md.h" @@ -152,6 +153,128 @@ int mbedtls_x509write_crt_set_validity(mbedtls_x509write_cert *ctx, return 0; } + +int mbedtls_x509write_crt_set_subject_alternative_name(mbedtls_x509write_cert *ctx, + const mbedtls_x509_san_list *san_list) +{ + int ret = 0; + const mbedtls_x509_san_list *cur; + unsigned char *buf; + unsigned char *p; + size_t len; + size_t buflen = 0; + + /* Determine the maximum size of the SubjectAltName list */ + for (cur = san_list; cur != NULL; cur = cur->next) { + /* Calculate size of the required buffer */ + switch (cur->node.type) { + case MBEDTLS_X509_SAN_DNS_NAME: + case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: + case MBEDTLS_X509_SAN_IP_ADDRESS: + /* length of value for each name entry, + * maximum 4 bytes for the length field, + * 1 byte for the tag/type. + */ + buflen += cur->node.san.unstructured_name.len + 4 + 1; + break; + case MBEDTLS_X509_SAN_DIRECTORY_NAME: + const mbedtls_asn1_named_data *chunk = &cur->node.san.directory_name; + while (chunk != NULL) { + // 5 bytes for OID, max 4 bytes for length, +1 for tag, + // additional 4 max for length, +1 for tag. + // See x509_write_name for more information. + buflen += chunk->val.len + 5 + 4 + 1 + 4 + 1; + chunk = chunk->next; + } + buflen += cur->node.san.unstructured_name.len + 4 + 1; + break; + default: + /* Not supported - skip. */ + break; + } + } + + /* Add the extra length field and tag */ + buflen += 4 + 1; + + /* Allocate buffer */ + buf = mbedtls_calloc(1, buflen); + if (buf == NULL) { + return MBEDTLS_ERR_ASN1_ALLOC_FAILED; + } + + mbedtls_platform_zeroize(buf, buflen); + p = buf + buflen; + + /* Write ASN.1-based structure */ + cur = san_list; + len = 0; + while (cur != NULL) { + size_t single_san_len = 0; + switch (cur->node.type) { + case MBEDTLS_X509_SAN_DNS_NAME: + case MBEDTLS_X509_SAN_RFC822_NAME: + case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: + case MBEDTLS_X509_SAN_IP_ADDRESS: + { + const unsigned char *unstructured_name = + (const unsigned char *) cur->node.san.unstructured_name.p; + size_t unstructured_name_len = cur->node.san.unstructured_name.len; + + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, + mbedtls_asn1_write_raw_buffer( + &p, buf, + unstructured_name, unstructured_name_len)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, mbedtls_asn1_write_len( + &p, buf, unstructured_name_len)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, + mbedtls_asn1_write_tag( + &p, buf, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | cur->node.type)); + } + break; + case MBEDTLS_X509_SAN_DIRECTORY_NAME: + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, + mbedtls_x509_write_names(&p, buf, + (mbedtls_asn1_named_data *) & + cur->node + .san.directory_name)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, + mbedtls_asn1_write_len(&p, buf, single_san_len)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(single_san_len, + mbedtls_asn1_write_tag(&p, buf, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_X509_SAN_DIRECTORY_NAME)); + break; + default: + /* Skip unsupported names. */ + break; + } + cur = cur->next; + len += single_san_len; + } + + MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&p, buf, len)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, + mbedtls_asn1_write_tag(&p, buf, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + ret = mbedtls_x509write_crt_set_extension( + ctx, + MBEDTLS_OID_SUBJECT_ALT_NAME, + MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME), + 0, + buf + buflen - len, + len); + +cleanup: + mbedtls_free(buf); + return ret; +} + + int mbedtls_x509write_crt_set_extension(mbedtls_x509write_cert *ctx, const char *oid, size_t oid_len, int critical,