diff --git a/CMakeLists.txt b/CMakeLists.txt index 5510b09831c..7bacef27443 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1054,6 +1054,7 @@ list(INSERT plugin_sources 1 ${PROJECT_SOURCE_DIR}/plugins/crypto/mbedtls/ua_securitypolicy_basic256sha256.c ${PROJECT_SOURCE_DIR}/plugins/crypto/mbedtls/ua_securitypolicy_aes128sha256rsaoaep.c ${PROJECT_SOURCE_DIR}/plugins/crypto/mbedtls/ua_securitypolicy_aes256sha256rsapss.c + ${PROJECT_SOURCE_DIR}/plugins/crypto/mbedtls/ua_mbedtls_create_certificate.c ${PROJECT_SOURCE_DIR}/plugins/crypto/mbedtls/ua_pki_mbedtls.c) endif() diff --git a/examples/encryption/server_encryption.c b/examples/encryption/server_encryption.c index 3e9f9cebc17..996709cb938 100644 --- a/examples/encryption/server_encryption.c +++ b/examples/encryption/server_encryption.c @@ -38,7 +38,6 @@ int main(int argc, char* argv[]) { "Missing arguments. Arguments are " " " "[, ...]"); -#if defined(UA_ENABLE_ENCRYPTION_OPENSSL) || defined(UA_ENABLE_ENCRYPTION_LIBRESSL) UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Trying to create a certificate."); UA_String subject[3] = {UA_STRING_STATIC("C=DE"), @@ -65,9 +64,6 @@ int main(int argc, char* argv[]) { UA_StatusCode_name(statusCertGen)); return EXIT_SUCCESS; } -#else - return EXIT_SUCCESS; -#endif } diff --git a/plugins/crypto/mbedtls/ua_mbedtls_create_certificate.c b/plugins/crypto/mbedtls/ua_mbedtls_create_certificate.c new file mode 100644 index 00000000000..74a874738df --- /dev/null +++ b/plugins/crypto/mbedtls/ua_mbedtls_create_certificate.c @@ -0,0 +1,569 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) 2023 Fraunhofer IOSB (Author: Noel Graf) + * + */ + +#include +#include + +#if defined(UA_ENABLE_ENCRYPTION_MBEDTLS) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if MBEDTLS_VERSION_NUMBER < 0x03030000 +#define SET_OID(x, oid) \ + do { x.len = MBEDTLS_OID_SIZE(oid); x.p = (unsigned char *) oid; } while (0) +#endif + +#if MBEDTLS_VERSION_NUMBER < 0x03050000 +#define CHECK_OVERFLOW_ADD(a, b) \ + do \ + { \ + if (a > SIZE_MAX - (b)) \ + { \ + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; \ + } \ + a += b; \ + } while (0) + +#define MBEDTLS_ASN1_CHK_CLEANUP_ADD(g, f) \ + do \ + { \ + if ((ret = (f)) < 0) \ + goto cleanup; \ + else \ + (g) += ret; \ + } while (0) + +typedef struct mbedtls_x509_subject_alternative_name_tmp { + int type; /**< The SAN type, value of MBEDTLS_X509_SAN_XXX. */ + union { + mbedtls_x509_san_other_name other_name; + mbedtls_x509_name directory_name; + mbedtls_x509_buf unstructured_name; /**< The buffer for the unstructured types. rfc822Name, dnsName and uniformResourceIdentifier are currently supported. */ + } + san; /**< A union of the supported SAN types */ +} mbedtls_x509_subject_alternative_name_tmp; + +typedef struct mbedtls_x509_san_list { + mbedtls_x509_subject_alternative_name_tmp node; + struct mbedtls_x509_san_list *next; +} mbedtls_x509_san_list; + + +int mbedtls_x509_write_set_san_common(mbedtls_asn1_named_data **extensions, + const mbedtls_x509_san_list *san_list); + +int mbedtls_x509write_crt_set_subject_alternative_name(mbedtls_x509write_cert *ctx, + const mbedtls_x509_san_list *san_list); +#endif + +#if MBEDTLS_VERSION_NUMBER < 0x03030000 +int mbedtls_x509write_crt_set_ext_key_usage(mbedtls_x509write_cert *ctx, + const mbedtls_asn1_sequence *exts); +#endif + +static int write_certificate(mbedtls_x509write_cert *crt, UA_CertificateFormat certFormat, + UA_ByteString *outCertificate, int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +static int write_private_key(mbedtls_pk_context *key, UA_CertificateFormat keyFormat, UA_ByteString *outPrivateKey); + + UA_StatusCode +UA_CreateCertificate(const UA_Logger *logger, const UA_String *subject, + size_t subjectSize, const UA_String *subjectAltName, + size_t subjectAltNameSize, UA_CertificateFormat certFormat, + UA_KeyValueMap *params, UA_ByteString *outPrivateKey, + UA_ByteString *outCertificate) { + if(!outPrivateKey || !outCertificate || !logger || !subjectAltName || !subject || + subjectAltNameSize == 0 || subjectSize == 0 || + (certFormat != UA_CERTIFICATEFORMAT_DER && certFormat != UA_CERTIFICATEFORMAT_PEM)) + return UA_STATUSCODE_BADINVALIDARGUMENT; + + /* Use the maximum size */ + UA_UInt16 keySizeBits = 4096; + /* Default to 1 year */ + UA_UInt16 expiresInDays = 365; + + if(params) { + const UA_UInt16 *keySizeBitsValue = (const UA_UInt16 *)UA_KeyValueMap_getScalar( + params, UA_QUALIFIEDNAME(0, "key-size-bits"), &UA_TYPES[UA_TYPES_UINT16]); + if(keySizeBitsValue) + keySizeBits = *keySizeBitsValue; + + const UA_UInt16 *expiresInDaysValue = (const UA_UInt16 *)UA_KeyValueMap_getScalar( + params, UA_QUALIFIEDNAME(0, "expires-in-days"), &UA_TYPES[UA_TYPES_UINT16]); + if(expiresInDaysValue) + expiresInDays = *expiresInDaysValue; + } + + UA_ByteString_init(outPrivateKey); + UA_ByteString_init(outCertificate); + + UA_Int32 serial = 1; + + mbedtls_pk_context key; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_entropy_context entropy; + const char *pers = "gen_key"; + mbedtls_x509write_cert crt; + + UA_StatusCode errRet = UA_STATUSCODE_GOOD; + + /* Set to sane values */ + mbedtls_pk_init(&key); + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_entropy_init(&entropy); + mbedtls_x509write_crt_init(&crt); + + /* Seed the random number generator */ + if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *)pers, strlen(pers)) != 0) { + UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, + "Failed to initialize the random number generator."); + errRet = UA_STATUSCODE_BADINTERNALERROR; + goto cleanup; + } + + /* Generate an RSA key pair */ + if (mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)) != 0 || + mbedtls_rsa_gen_key(mbedtls_pk_rsa(key), mbedtls_ctr_drbg_random, &ctr_drbg, keySizeBits, 65537) != 0) { + UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, + "Failed to generate RSA key pair."); + errRet = UA_STATUSCODE_BADINTERNALERROR; + goto cleanup; + } + + /* Setting certificate values */ + mbedtls_x509write_crt_set_version(&crt, MBEDTLS_X509_CRT_VERSION_3); + mbedtls_x509write_crt_set_md_alg(&crt, MBEDTLS_MD_SHA256); + + size_t subject_char_len = 0; + for(size_t i = 0; i < subjectSize; i++) { + subject_char_len += subject[i].length; + } + char *subject_char = (char*)UA_malloc(subject_char_len + subjectSize); + if(!subject_char) { + UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, + "Cannot allocate memory for subject. Out of memory."); + errRet = UA_STATUSCODE_BADOUTOFMEMORY; + goto cleanup; + } + + size_t pos = 0; + for(size_t i = 0; i < subjectSize; i++) { + subject_char_len += subject[i].length; + memcpy(subject_char + pos, subject[i].data, subject[i].length); + pos += subject[i].length; + if(i < subjectSize - 1) + subject_char[pos++] = ','; + else + subject_char[pos++] = '\0'; + } + + if((mbedtls_x509write_crt_set_subject_name(&crt, subject_char)) != 0) { + UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, + "Setting subject failed."); + errRet = UA_STATUSCODE_BADINTERNALERROR; + UA_free(subject_char); + goto cleanup; + } + + if((mbedtls_x509write_crt_set_issuer_name(&crt, subject_char)) != 0) { + UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, + "Setting issuer failed."); + errRet = UA_STATUSCODE_BADINTERNALERROR; + UA_free(subject_char); + goto cleanup; + } + + UA_free(subject_char); + + mbedtls_x509_san_list *cur; + cur = (mbedtls_x509_san_list*)mbedtls_calloc(1, sizeof(mbedtls_x509_san_list)); + cur->next = NULL; + cur->node.type = MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER; + cur->node.san.unstructured_name.p = (unsigned char *) subjectAltName[1].data; + cur->node.san.unstructured_name.len = subjectAltName[1].length; + + cur->next = (mbedtls_x509_san_list*)mbedtls_calloc(1, sizeof(mbedtls_x509_san_list)); + cur->next->next = NULL; + cur->next->node.type = MBEDTLS_X509_SAN_DNS_NAME; + cur->next->node.san.unstructured_name.p = (unsigned char *) subjectAltName[0].data; + cur->next->node.san.unstructured_name.len = subjectAltName[0].length; + + if((mbedtls_x509write_crt_set_subject_alternative_name(&crt, cur)) != 0) { + UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, + "Setting subject alternative name failed."); + errRet = UA_STATUSCODE_BADINTERNALERROR; + mbedtls_free(cur->next); + mbedtls_free(cur); + goto cleanup; + } + + mbedtls_free(cur->next); + mbedtls_free(cur); + + mbedtls_mpi serial_mpi; + mbedtls_mpi_init(&serial_mpi); + mbedtls_mpi_lset(&serial_mpi, serial); + mbedtls_x509write_crt_set_serial(&crt, &serial_mpi); + + mbedtls_mpi_free(&serial_mpi); + + /* Get the current time */ + time_t rawTime; + struct tm *timeInfo; + time(&rawTime); + timeInfo = gmtime(&rawTime); + + /* Format the current timestamp */ + char current_timestamp[15]; // YYYYMMDDhhmmss + '\0' + strftime(current_timestamp, sizeof(current_timestamp), "%Y%m%d%H%M%S", timeInfo); + + /* Calculate the future timestamp */ + timeInfo->tm_mday += expiresInDays; + time_t future_time = mktime(timeInfo); + + /* Format the future timestamp */ + char future_timestamp[15]; // YYYYMMDDhhmmss + '\0' + strftime(future_timestamp, sizeof(future_timestamp), "%Y%m%d%H%M%S", gmtime(&future_time)); + + if(mbedtls_x509write_crt_set_validity(&crt, current_timestamp, future_timestamp) != 0) { + UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, + "Setting 'not before' and 'not after' failed."); + errRet = UA_STATUSCODE_BADINTERNALERROR; + goto cleanup; + } + + if(mbedtls_x509write_crt_set_basic_constraints(&crt, 0, -1) != 0) { + UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, + "Setting basic constraints failed."); + errRet = UA_STATUSCODE_BADINTERNALERROR; + goto cleanup; + } + + if(mbedtls_x509write_crt_set_key_usage(&crt, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_NON_REPUDIATION + | MBEDTLS_X509_KU_KEY_ENCIPHERMENT | MBEDTLS_X509_KU_DATA_ENCIPHERMENT + | MBEDTLS_X509_KU_KEY_CERT_SIGN | MBEDTLS_X509_KU_CRL_SIGN) != 0) { + UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, + "Setting key usage failed."); + errRet = UA_STATUSCODE_BADINTERNALERROR; + goto cleanup; + } + + mbedtls_asn1_sequence *ext_key_usage; + ext_key_usage = (mbedtls_asn1_sequence *)mbedtls_calloc(1, sizeof(mbedtls_asn1_sequence)); + ext_key_usage->buf.tag = MBEDTLS_ASN1_OID; + SET_OID(ext_key_usage->buf, MBEDTLS_OID_SERVER_AUTH); + ext_key_usage->next = (mbedtls_asn1_sequence *)mbedtls_calloc(1, sizeof(mbedtls_asn1_sequence)); + ext_key_usage->next->buf.tag = MBEDTLS_ASN1_OID; + SET_OID(ext_key_usage->next->buf, MBEDTLS_OID_CLIENT_AUTH); + + if(mbedtls_x509write_crt_set_ext_key_usage(&crt, ext_key_usage) != 0) { + UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, + "Setting extended key usage failed."); + errRet = UA_STATUSCODE_BADINTERNALERROR; + mbedtls_free(ext_key_usage->next); + mbedtls_free(ext_key_usage); + goto cleanup; + } + + mbedtls_free(ext_key_usage->next); + mbedtls_free(ext_key_usage); + + mbedtls_x509write_crt_set_subject_key(&crt, &key); + mbedtls_x509write_crt_set_issuer_key(&crt, &key); + + + /* Write private key */ + if ((write_private_key(&key, certFormat, outPrivateKey)) != 0) { + UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, + "Create Certificate: Writing private key failed."); + errRet = UA_STATUSCODE_BADINTERNALERROR; + goto cleanup; + } + + /* Write Certificate */ + if ((write_certificate(&crt, certFormat, outCertificate, + mbedtls_ctr_drbg_random, &ctr_drbg)) != 0) { + UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, + "Create Certificate: Writing certificate failed."); + errRet = UA_STATUSCODE_BADINTERNALERROR; + goto cleanup; + } + + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + mbedtls_x509write_crt_free(&crt); + mbedtls_pk_free(&key); + +cleanup: + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + mbedtls_x509write_crt_free(&crt); + mbedtls_pk_free(&key); + return errRet; +} + +static int write_private_key(mbedtls_pk_context *key, UA_CertificateFormat keyFormat, UA_ByteString *outPrivateKey) { + int ret; + unsigned char output_buf[16000]; + unsigned char *c = output_buf; + size_t len = 0; + + memset(output_buf, 0, 16000); + switch(keyFormat) { + case UA_CERTIFICATEFORMAT_DER: { + if((ret = mbedtls_pk_write_key_pem(key, output_buf, 16000)) != 0) { + return ret; + } + + len = strlen((char *) output_buf); + break; + } + case UA_CERTIFICATEFORMAT_PEM: { + if((ret = mbedtls_pk_write_key_der(key, output_buf, 16000)) < 0) { + return ret; + } + + len = ret; + c = output_buf + sizeof(output_buf) - len; + break; + } + } + + outPrivateKey->length = len; + UA_ByteString_allocBuffer(outPrivateKey, outPrivateKey->length); + memcpy(outPrivateKey->data, c, outPrivateKey->length); + + return 0; +} + +static int write_certificate(mbedtls_x509write_cert *crt, UA_CertificateFormat certFormat, + UA_ByteString *outCertificate, int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) { + int ret; + unsigned char output_buf[4096]; + unsigned char *c = output_buf; + size_t len = 0; + + memset(output_buf, 0, 4096); + switch(certFormat) { + case UA_CERTIFICATEFORMAT_DER: { + if((ret = mbedtls_x509write_crt_der(crt, output_buf, 4096, f_rng, p_rng)) < 0) { + return ret; + } + + len = ret; + c = output_buf + 4096 - len; + break; + } + case UA_CERTIFICATEFORMAT_PEM: { + if((ret = mbedtls_x509write_crt_pem(crt, output_buf, 4096, f_rng, p_rng)) < 0) { + return ret; + } + + len = strlen((char *)output_buf); + break; + } + } + + outCertificate->length = len; + UA_ByteString_allocBuffer(outCertificate, outCertificate->length); + memcpy(outCertificate->data, c, outCertificate->length); + + return 0; +} + +#if MBEDTLS_VERSION_NUMBER < 0x03030000 +int mbedtls_x509write_crt_set_ext_key_usage(mbedtls_x509write_cert *ctx, + const mbedtls_asn1_sequence *exts) { + unsigned char buf[256]; + unsigned char *c = buf + sizeof(buf); + int ret; + size_t len = 0; + const mbedtls_asn1_sequence *last_ext = NULL; + const mbedtls_asn1_sequence *ext; + + memset(buf, 0, sizeof(buf)); + + /* We need at least one extension: SEQUENCE SIZE (1..MAX) OF KeyPurposeId */ + if (exts == NULL) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + /* Iterate over exts backwards, so we write them out in the requested order */ + while (last_ext != exts) { + for (ext = exts; ext->next != last_ext; ext = ext->next) { + } + if (ext->buf.tag != MBEDTLS_ASN1_OID) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(&c, buf, ext->buf.p, ext->buf.len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, ext->buf.len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_OID)); + last_ext = ext; + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_tag(&c, buf, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); + + return mbedtls_x509write_crt_set_extension(ctx, + MBEDTLS_OID_EXTENDED_KEY_USAGE, + MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE), + 1, c, len); +} + +#endif + +#if MBEDTLS_VERSION_NUMBER < 0x03050000 +int mbedtls_x509write_crt_set_subject_alternative_name(mbedtls_x509write_cert *ctx, + const mbedtls_x509_san_list *san_list) { + return mbedtls_x509_write_set_san_common(&ctx->extensions, san_list); +} + +int mbedtls_x509_write_set_san_common(mbedtls_asn1_named_data **extensions, + 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: + case MBEDTLS_X509_SAN_RFC822_NAME: + /* length of value for each name entry, + * maximum 4 bytes for the length field, + * 1 byte for the tag/type. + */ + CHECK_OVERFLOW_ADD(buflen, cur->node.san.unstructured_name.len); + CHECK_OVERFLOW_ADD(buflen, 4 + 1); + break; + case MBEDTLS_X509_SAN_DIRECTORY_NAME: + { + const mbedtls_asn1_named_data *chunk = &cur->node.san.directory_name; + while (chunk != NULL) { + // Max 4 bytes for length, +1 for tag, + // additional 4 max for length, +1 for tag. + // See x509_write_name for more information. + CHECK_OVERFLOW_ADD(buflen, 4 + 1 + 4 + 1); + CHECK_OVERFLOW_ADD(buflen, chunk->oid.len); + CHECK_OVERFLOW_ADD(buflen, chunk->val.len); + chunk = chunk->next; + } + CHECK_OVERFLOW_ADD(buflen, 4 + 1); + break; + } + default: + /* Not supported - return. */ + return MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; + } + } + + /* Add the extra length field and tag */ + CHECK_OVERFLOW_ADD(buflen, 4 + 1); + + /* Allocate buffer */ + buf = (unsigned char *)mbedtls_calloc(1, buflen); + if (buf == NULL) { + return MBEDTLS_ERR_ASN1_ALLOC_FAILED; + } + 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 *)(uintptr_t)&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: + /* Error out on an unsupported SAN */ + ret = MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; + goto cleanup; + } + cur = cur->next; + /* check for overflow */ + if (len > SIZE_MAX - single_san_len) { + ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA; + goto cleanup; + } + 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_x509_set_extension(extensions, + MBEDTLS_OID_SUBJECT_ALT_NAME, + MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME), + 0, + buf + buflen - len, len); + + /* If we exceeded the allocated buffer it means that maximum size of the SubjectAltName list + * was incorrectly calculated and memory is corrupted. */ + if (p < buf) { + ret = MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; + } +cleanup: + mbedtls_free(buf); + return ret; +} +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4a2be645fa4..7667a46c1c6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -113,6 +113,7 @@ if(UA_ENABLE_ENCRYPTION_MBEDTLS) ${PROJECT_SOURCE_DIR}/plugins/crypto/mbedtls/ua_securitypolicy_basic256sha256.c ${PROJECT_SOURCE_DIR}/plugins/crypto/mbedtls/ua_securitypolicy_aes128sha256rsaoaep.c ${PROJECT_SOURCE_DIR}/plugins/crypto/mbedtls/ua_securitypolicy_aes256sha256rsapss.c + ${PROJECT_SOURCE_DIR}/plugins/crypto/mbedtls/ua_mbedtls_create_certificate.c ${PROJECT_SOURCE_DIR}/plugins/crypto/mbedtls/ua_pki_mbedtls.c) endif() @@ -432,6 +433,7 @@ if(UA_ENABLE_ENCRYPTION_MBEDTLS) ua_add_test(encryption/check_encryption_basic256sha256.c) ua_add_test(encryption/check_encryption_aes128sha256rsaoaep.c) ua_add_test(encryption/check_encryption_aes256sha256rsapss.c) + ua_add_test(encryption/check_cert_generation.c) endif() if(UA_ENABLE_ENCRYPTION_MBEDTLS AND UA_ENABLE_CERT_REJECTED_DIR)