diff --git a/src/pk_ec.c b/src/pk_ec.c index f577de95db3..fe78561f5b6 100644 --- a/src/pk_ec.c +++ b/src/pk_ec.c @@ -4749,6 +4749,79 @@ int wolfSSL_EC_KEY_set_public_key(WOLFSSL_EC_KEY *key, return ret; } +/* + * Decode an octet-encoded EC public point into @key. + * + * The point conversion form of @key is set from the encoding byte so that + * re-encoding with wolfSSL_i2o_ECPublicKey() reproduces @buf. Hybrid + * encodings have no wolfSSL equivalent and leave the form unchanged. + * + * Return code compliant with OpenSSL. + * + * @param [in, out] key EC key (must already have a group set). + * @param [in] buf Octet-encoded public point. + * @param [in] len Length of @buf in bytes. + * @param [in] ctx BN context. May be NULL. + * @return 1 on success. + * @return 0 on failure. + */ +int wolfSSL_EC_KEY_oct2key(WOLFSSL_EC_KEY *key, const unsigned char *buf, + size_t len, WOLFSSL_BN_CTX *ctx) +{ + int ret = 1; + const WOLFSSL_EC_GROUP *group = NULL; + WOLFSSL_EC_POINT *point = NULL; + + WOLFSSL_ENTER("wolfSSL_EC_KEY_oct2key"); + + if ((key == NULL) || (buf == NULL) || (len == 0)) { + WOLFSSL_MSG("wolfSSL_EC_KEY_oct2key Bad arguments"); + ret = 0; + } + + if (ret == 1) { + group = wolfSSL_EC_KEY_get0_group(key); + if (group == NULL) { + WOLFSSL_MSG("EC_KEY has no group set"); + ret = 0; + } + } + + if (ret == 1) { + point = wolfSSL_EC_POINT_new((WOLFSSL_EC_GROUP*)group); + if (point == NULL) { + WOLFSSL_MSG("wolfSSL_EC_POINT_new failed"); + ret = 0; + } + } + + if ((ret == 1) && + (wolfSSL_EC_POINT_oct2point(group, point, buf, len, ctx) != 1)) { + WOLFSSL_MSG("wolfSSL_EC_POINT_oct2point failed"); + ret = 0; + } + + if ((ret == 1) && (wolfSSL_EC_KEY_set_public_key(key, point) != 1)) { + WOLFSSL_MSG("wolfSSL_EC_KEY_set_public_key failed"); + ret = 0; + } + + if (ret == 1) { + /* SEC 1: 0x02/0x03 compressed, 0x04 uncompressed. Clearing the low + * bit turns the leading byte into the conversion form. Any other + * leading byte - hybrid, or the single 0x00 of the point at infinity - + * names no form wolfSSL_EC_KEY_set_conv_form() takes, so leave the + * key's form alone rather than handing it a value it rejects. */ + if ((buf[0] == 0x02) || (buf[0] == 0x03) || (buf[0] == 0x04)) { + wolfSSL_EC_KEY_set_conv_form(key, buf[0] & ~0x01); + } + } + + wolfSSL_EC_POINT_free(point); + + return ret; +} + #ifndef NO_WOLFSSL_STUB /* Set the ASN.1 encoding flag against the EC key. * diff --git a/src/x509.c b/src/x509.c index d9e948fe943..9bb82151608 100644 --- a/src/x509.c +++ b/src/x509.c @@ -1385,6 +1385,155 @@ static int wolfssl_x509_add_custom_ext(WOLFSSL_X509 *x509, } #endif /* WOLFSSL_CUSTOM_OID */ +/* Map a DER encoded KeyPurposeId to its EXTKEYUSE_* bit. + * + * @param [in] der DER encoded OBJECT IDENTIFIER. + * @param [in] derSz Length of @der in bytes. + * @param [out] bit Usage bit, 0 when wolfSSL tracks no bit for the OID. + * @return 0 on success, ASN_PARSE_E when @der is not one OBJECT IDENTIFIER. + */ +static int wolfssl_eku_der_to_bit(const byte* der, word32 derSz, byte* bit) +{ + word32 idx = 0; + word32 oid = 0; + + *bit = 0; + + /* wolfSSL_d2i_ASN1_OBJECT() reads back at most this much. */ + if (derSz > (word32)(MAX_OID_SZ + 2)) { + return ASN_PARSE_E; + } + if (GetObjectId(der, &idx, &oid, oidIgnoreType, derSz) != 0) { + return ASN_PARSE_E; + } + /* One OID, no trailing bytes. */ + if (idx != derSz) { + return ASN_PARSE_E; + } + + switch (oid) { + case EKU_ANY_OID: + *bit = EXTKEYUSE_ANY; + break; + case EKU_SERVER_AUTH_OID: + *bit = EXTKEYUSE_SERVER_AUTH; + break; + case EKU_CLIENT_AUTH_OID: + *bit = EXTKEYUSE_CLIENT_AUTH; + break; + case EKU_CODESIGNING_OID: + *bit = EXTKEYUSE_CODESIGN; + break; + case EKU_EMAILPROTECT_OID: + *bit = EXTKEYUSE_EMAILPROT; + break; + case EKU_TIMESTAMP_OID: + *bit = EXTKEYUSE_TIMESTAMP; + break; + case EKU_OCSP_SIGN_OID: + *bit = EXTKEYUSE_OCSP_SIGN; + break; + default: + /* Unknown OID: kept in the list, no bit. */ + break; + } + + return 0; +} + +/* Check that every entry of a KeyPurposeId stack is one DER OBJECT + * IDENTIFIER, reporting the size of the encoded list and the usage bits. + * + * @return WOLFSSL_SUCCESS on success, WOLFSSL_FAILURE otherwise. + */ +static int wolfssl_x509_eku_sk_check(WOLFSSL_STACK* sk, word32* derSz, + byte* usage) +{ + int i; + int num; + word32 sz = 0; + byte bits = 0; + byte bit = 0; + + num = wolfSSL_sk_num(sk); + if (num <= 0) { + WOLFSSL_MSG("extKeyUsage object stack is empty"); + return WOLFSSL_FAILURE; + } + + for (i = 0; i < num; i++) { + WOLFSSL_ASN1_OBJECT* obj = (WOLFSSL_ASN1_OBJECT*)wolfSSL_sk_value(sk, + i); + + if ((obj == NULL) || (obj->obj == NULL) || (obj->objSz == 0)) { + WOLFSSL_MSG("extKeyUsage entry has no OID encoding"); + return WOLFSSL_FAILURE; + } + if (wolfssl_eku_der_to_bit(obj->obj, obj->objSz, &bit) != 0) { + WOLFSSL_MSG("extKeyUsage entry is not a KeyPurposeId"); + return WOLFSSL_FAILURE; + } + bits |= bit; + sz += obj->objSz; + } + + *derSz = sz; + *usage = bits; + + return WOLFSSL_SUCCESS; +} + +/* Set the extKeyUsage of @x509 from the stack of ASN.1 OBJECTs that + * wolfSSL_X509V3_EXT_i2d() builds for WC_NID_ext_key_usage. + * + * The KeyPurposeId list is stored as the bare concatenation of its DER + * OBJECT IDENTIFIERs, the layout DecodeExtKeyUsage() hands to WOLFSSL_X509, + * so that wolfSSL_X509_get_ext_d2i() reads it back. OIDs wolfSSL has no bit + * for stay in the list. + * + * @return WOLFSSL_SUCCESS on success, WOLFSSL_FAILURE otherwise. + */ +static int wolfssl_x509_add_ext_key_usage_sk(WOLFSSL_X509* x509, + WOLFSSL_X509_EXTENSION* ext) +{ + int i; + int num; + byte* der; + word32 derSz = 0; + word32 idx = 0; + byte usage = 0; + + /* Validate before allocating so a bad entry changes nothing. */ + if (wolfssl_x509_eku_sk_check(ext->ext_sk, &derSz, &usage) != + WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + num = wolfSSL_sk_num(ext->ext_sk); + + der = (byte*)XMALLOC(derSz, x509->heap, DYNAMIC_TYPE_X509_EXT); + if (der == NULL) { + WOLFSSL_MSG("Memory allocation failure"); + return WOLFSSL_FAILURE; + } + + for (i = 0; i < num; i++) { + WOLFSSL_ASN1_OBJECT* obj = (WOLFSSL_ASN1_OBJECT*)wolfSSL_sk_value( + ext->ext_sk, i); + + XMEMCPY(der + idx, obj->obj, obj->objSz); + idx += obj->objSz; + } + + XFREE(x509->extKeyUsageSrc, x509->heap, DYNAMIC_TYPE_X509_EXT); + x509->extKeyUsageSrc = der; + x509->extKeyUsageSz = derSz; + x509->extKeyUsageCount = (word32)num; + x509->extKeyUsage = usage; + x509->extKeyUsageCrit = (byte)ext->crit; + + return WOLFSSL_SUCCESS; +} + int wolfSSL_X509_add_ext(WOLFSSL_X509 *x509, WOLFSSL_X509_EXTENSION *ext, int loc) { @@ -1473,6 +1622,14 @@ int wolfSSL_X509_add_ext(WOLFSSL_X509 *x509, WOLFSSL_X509_EXTENSION *ext, return WOLFSSL_FAILURE; } } + else if (ext && ext->ext_sk != NULL) { + if (wolfssl_x509_add_ext_key_usage_sk(x509, ext) != + WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + } + /* No data and no object stack: nothing to set, treat as no-op like the + * WC_NID_key_usage case above. */ break; case WC_NID_basic_constraints: if (ext->obj) { @@ -3510,8 +3667,10 @@ WOLFSSL_X509_EXTENSION *wolfSSL_X509V3_EXT_i2d(int nid, int crit, /* WOLFSSL_BASIC_CONSTRAINTS */ WOLFSSL_BASIC_CONSTRAINTS* bc = (WOLFSSL_BASIC_CONSTRAINTS*)data; - if (!(ext->obj = wolfSSL_ASN1_OBJECT_new())) { - WOLFSSL_MSG("wolfSSL_ASN1_OBJECT_new failed"); + /* Build from the NID like the other cases: a bare ASN1_OBJECT has + * type 0, which wolfSSL_X509_add_ext() rejects. */ + if (!(ext->obj = wolfSSL_OBJ_nid2obj(nid))) { + WOLFSSL_MSG("wolfSSL_OBJ_nid2obj failed"); goto err_cleanup; } @@ -3584,6 +3743,242 @@ WOLFSSL_X509_EXTENSION *wolfSSL_X509V3_EXT_i2d(int nid, int crit, return NULL; } +/* Report whether an extension of type @nid is present in the in-memory @x509, + * based on wolfSSL's typed extension storage. Only the NIDs that + * wolfSSL_X509_add_ext() can consume have such storage. + * + * @return 1 when present, 0 when absent and -1 when @nid has no typed storage + * and presence therefore cannot be determined. */ +static int wolfssl_x509_ext_is_set(const WOLFSSL_X509 *x509, int nid) +{ + switch (nid) { + case WC_NID_subject_alt_name: + return (x509->subjAltNameSet != 0) || (x509->altNames != NULL); + case WC_NID_key_usage: + return x509->keyUsageSet != 0; + case WC_NID_ext_key_usage: + return (x509->extKeyUsage != 0) || (x509->extKeyUsageSrc != NULL); + case WC_NID_basic_constraints: + return x509->basicConstSet != 0; + case WC_NID_subject_key_identifier: + return (x509->subjKeyId != NULL) || (x509->subjKeyIdSet != 0); + case WC_NID_authority_key_identifier: + return (x509->authKeyId != NULL) || (x509->authKeyIdSet != 0); + default: + return -1; + } +} + +/* Remove the extension of type @nid from the in-memory @x509 by clearing the + * matching typed storage (mirrors the frees in FreeX509()). Every field the + * accessors for @nid look at has to be cleared, including the cached and + * "is set" flags, or the removed extension stays observable. + * + * The encoded certificate (x509->derCert) is not re-generated, so APIs that + * decode the DER again - wolfSSL_X509_get_ext_count(), + * wolfSSL_X509_get_ext_by_NID() - keep reporting the original extensions. + * That limitation is shared with wolfSSL_X509_add_ext(). + * + * Returns WOLFSSL_SUCCESS if @nid is a supported, removable extension, + * otherwise WOLFSSL_FAILURE. */ +static int wolfssl_x509_remove_ext(WOLFSSL_X509 *x509, int nid) +{ + switch (nid) { + case WC_NID_subject_alt_name: + if (x509->altNames != NULL) { + FreeAltNames(x509->altNames, x509->heap); + x509->altNames = NULL; + } + /* Retrieval hint indexes into the list just freed. */ + x509->altNamesNext = NULL; + #ifdef OPENSSL_ALL + XFREE(x509->subjAltNameSrc, x509->heap, DYNAMIC_TYPE_X509_EXT); + x509->subjAltNameSrc = NULL; + x509->subjAltNameSz = 0; + #endif + x509->subjAltNameSet = 0; + x509->subjAltNameCrit = 0; + break; + case WC_NID_key_usage: + x509->keyUsage = 0; + x509->keyUsageCrit = 0; + x509->keyUsageSet = 0; + break; + case WC_NID_ext_key_usage: + XFREE(x509->extKeyUsageSrc, x509->heap, DYNAMIC_TYPE_X509_EXT); + x509->extKeyUsageSrc = NULL; + x509->extKeyUsageSz = 0; + x509->extKeyUsageCount = 0; + x509->extKeyUsage = 0; + x509->extKeyUsageCrit = 0; + break; + case WC_NID_basic_constraints: + x509->isCa = 0; + x509->pathLength = 0; + x509->basicConstCrit = 0; + x509->basicConstSet = 0; + x509->basicConstPlSet = 0; + x509->pathLengthSet = 0; + break; + case WC_NID_subject_key_identifier: + XFREE(x509->subjKeyId, x509->heap, DYNAMIC_TYPE_X509_EXT); + x509->subjKeyId = NULL; + x509->subjKeyIdSz = 0; + /* Cached ASN1_STRING handed out by get0_subject_key_id(). */ + wolfSSL_ASN1_STRING_free(x509->subjKeyIdStr); + x509->subjKeyIdStr = NULL; + x509->subjKeyIdSet = 0; + x509->subjKeyIdCrit = 0; + break; + case WC_NID_authority_key_identifier: + /* authKeyId may point into authKeyIdSrc; free the source first. */ + if (x509->authKeyIdSrc != NULL) { + XFREE(x509->authKeyIdSrc, x509->heap, DYNAMIC_TYPE_X509_EXT); + x509->authKeyIdSrc = NULL; + } + else { + XFREE(x509->authKeyId, x509->heap, DYNAMIC_TYPE_X509_EXT); + } + x509->authKeyId = NULL; + x509->authKeyIdSz = 0; + x509->authKeyIdSrcSz = 0; + x509->authKeyIdSet = 0; + x509->authKeyIdCrit = 0; + break; + default: + WOLFSSL_MSG("Extension NID not supported for removal"); + return WOLFSSL_FAILURE; + } + return WOLFSSL_SUCCESS; +} + +/** + * Encode @value as an extension of type @nid and add it to @x, honoring the + * OpenSSL X509V3_ADD_* operation selected by the low nibble of @flags: + * X509V3_ADD_DEFAULT - add, fail if already present + * X509V3_ADD_APPEND - add without checking for an existing one + * X509V3_ADD_REPLACE - replace any existing, else add + * X509V3_ADD_REPLACE_EXISTING - replace, fail if not already present + * X509V3_ADD_KEEP_EXISTING - keep existing (no-op if present), else add + * X509V3_ADD_DELETE - delete existing, fail if not present + * X509V3_ADD_SILENT is accepted but inert: this function reports nothing that + * could be suppressed, and the flag never changes the result. + * + * Presence can only be determined for the extensions wolfSSL_X509_add_ext() + * stores in typed fields. For any other NID every operation except + * X509V3_ADD_APPEND - the one defined as not checking - fails. + * + * @return WOLFSSL_SUCCESS on success, WOLFSSL_FAILURE otherwise. + */ +int wolfSSL_X509_add1_ext_i2d(WOLFSSL_X509 *x, int nid, void *value, + int crit, unsigned long flags) +{ + WOLFSSL_X509_EXTENSION *ext = NULL; + int ret; + unsigned long op; + int exists; + + WOLFSSL_ENTER("wolfSSL_X509_add1_ext_i2d"); + + if (x == NULL) { + WOLFSSL_MSG("Bad parameter"); + return WOLFSSL_FAILURE; + } + + op = flags & WOLFSSL_X509V3_ADD_OP_MASK; + exists = wolfssl_x509_ext_is_set(x, nid); + if (exists < 0) { + if (op != WOLFSSL_X509V3_ADD_APPEND) { + WOLFSSL_MSG("Extension presence not tracked for this NID"); + return WOLFSSL_FAILURE; + } + exists = 0; + } + + switch (op) { + case WOLFSSL_X509V3_ADD_DELETE: + if (!exists) { + WOLFSSL_MSG("No extension to delete (X509V3_ADD_DELETE)"); + return WOLFSSL_FAILURE; + } + return wolfssl_x509_remove_ext(x, nid); + case WOLFSSL_X509V3_ADD_DEFAULT: + if (exists) { + WOLFSSL_MSG("Extension already present (X509V3_ADD_DEFAULT)"); + return WOLFSSL_FAILURE; + } + break; + case WOLFSSL_X509V3_ADD_APPEND: + case WOLFSSL_X509V3_ADD_REPLACE: + break; + case WOLFSSL_X509V3_ADD_REPLACE_EXISTING: + if (!exists) { + WOLFSSL_MSG("No extension to replace " + "(X509V3_ADD_REPLACE_EXISTING)"); + return WOLFSSL_FAILURE; + } + break; + case WOLFSSL_X509V3_ADD_KEEP_EXISTING: + if (exists) { + /* Keep the existing extension; nothing to add. */ + return WOLFSSL_SUCCESS; + } + break; + default: + WOLFSSL_MSG("Unsupported X509V3_ADD_* operation"); + return WOLFSSL_FAILURE; + } + + if (value == NULL) { + WOLFSSL_MSG("Bad parameter"); + return WOLFSSL_FAILURE; + } + + /* Encode the new extension before dropping the old one so that a failure + * leaves the certificate untouched. */ + ext = wolfSSL_X509V3_EXT_i2d(nid, crit, value); + if (ext == NULL) { + return WOLFSSL_FAILURE; + } + + /* wolfSSL_X509_add_ext() dispatches on the encoded object, which for an + * authorityKeyIdentifier built from an issuer name is caller supplied. + * Refuse here rather than remove one extension and write another. */ + if (((ext->obj != NULL) ? ext->obj->type : ext->value.nid) != nid) { + WOLFSSL_MSG("Encoded extension is not the requested NID"); + wolfSSL_X509_EXTENSION_free(ext); + return WOLFSSL_FAILURE; + } + + /* wolfSSL_X509V3_EXT_i2d() copies an extKeyUsage stack without looking + * inside it, so encoding is not the check that matters here. Reject a bad + * stack before the existing extension is dropped for it. */ + if ((nid == WC_NID_ext_key_usage) && (ext->value.data == NULL) && + (ext->ext_sk != NULL)) { + word32 ekuSz = 0; + byte ekuUsage = 0; + + if (wolfssl_x509_eku_sk_check(ext->ext_sk, &ekuSz, &ekuUsage) != + WOLFSSL_SUCCESS) { + wolfSSL_X509_EXTENSION_free(ext); + return WOLFSSL_FAILURE; + } + } + + if (exists && ((op == WOLFSSL_X509V3_ADD_REPLACE) || + (op == WOLFSSL_X509V3_ADD_REPLACE_EXISTING))) { + if (wolfssl_x509_remove_ext(x, nid) != WOLFSSL_SUCCESS) { + wolfSSL_X509_EXTENSION_free(ext); + return WOLFSSL_FAILURE; + } + } + + ret = wolfSSL_X509_add_ext(x, ext, -1); + wolfSSL_X509_EXTENSION_free(ext); + + return ret; +} + /* Returns pointer to ASN1_OBJECT from an X509_EXTENSION object */ WOLFSSL_ASN1_OBJECT* wolfSSL_X509_EXTENSION_get_object( WOLFSSL_X509_EXTENSION* ext) diff --git a/tests/api.c b/tests/api.c index 94ed40fbfb5..f69c77b71cc 100644 --- a/tests/api.c +++ b/tests/api.c @@ -2781,6 +2781,36 @@ static int test_wolfSSL_set_cipher_list_exclusions(void) #undef TEST_CIPHER_EXCLUDE_NULL #endif +/* Test SSL_set_ciphersuites OpenSSL-compat macro. */ +static int test_wolfSSL_SSL_set_ciphersuites(void) +{ + EXPECT_DECLS; +/* BUILD_TLS_AES_128_GCM_SHA256 is what actually gates the suite; it implies + * WOLFSSL_TLS13 and HAVE_AESGCM. */ +#if defined(OPENSSL_EXTRA) && !defined(NO_WOLFSSL_CLIENT) && \ + defined(BUILD_TLS_AES_128_GCM_SHA256) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + /* cipher_names[] only carries the IANA name when error strings are built + * in, so lean builds must ask for the suite by its wolfSSL name. */ +#ifndef NO_ERROR_STRINGS + const char* suite = "TLS_AES_128_GCM_SHA256"; +#else + const char* suite = "TLS13-AES128-GCM-SHA256"; +#endif + + ExpectNotNull(ctx = SSL_CTX_new(wolfSSLv23_client_method())); + ExpectNotNull(ssl = SSL_new(ctx)); + + ExpectIntEQ(SSL_set_ciphersuites(ssl, suite), 1); + ExpectIntEQ(SSL_set_ciphersuites(ssl, "BOGUS-SUITE"), 0); + + SSL_free(ssl); + SSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + static int test_wolfSSL_set_alpn_protos_default_fails(void) { EXPECT_DECLS; @@ -22244,6 +22274,32 @@ static int test_wolfSSL_sk_GENERAL_NAME(void) return EXPECT_RESULT(); } +static int test_wolfSSL_sk_GENERAL_NAME_new_null(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) + STACK_OF(GENERAL_NAME)* sk = NULL; + GENERAL_NAME* gn = NULL; + + ExpectNotNull(sk = sk_GENERAL_NAME_new_null()); + ExpectIntEQ(sk_GENERAL_NAME_num(sk), 0); + + ExpectNotNull(gn = GENERAL_NAME_new()); + if (gn != NULL) { + ExpectIntEQ(sk_GENERAL_NAME_push(sk, gn), 1); + /* Stack does not own gn on push failure; free it. */ + if (EXPECT_FAIL()) { + GENERAL_NAME_free(gn); + gn = NULL; + } + } + ExpectIntEQ(sk_GENERAL_NAME_num(sk), 1); + + sk_GENERAL_NAME_pop_free(sk, GENERAL_NAME_free); +#endif + return EXPECT_RESULT(); +} + static int test_wolfSSL_GENERAL_NAME_print(void) { EXPECT_DECLS; @@ -22950,6 +23006,595 @@ static int test_wolfSSL_X509_set_extensions(void) return EXPECT_RESULT(); } +/* Test wolfSSL_X509_add1_ext_i2d using a SAN DNS entry. */ +static int test_wolfSSL_X509_add1_ext_i2d(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(OPENSSL_ALL) && !defined(NO_CERTS) && \ + !defined(NO_ASN) + WOLFSSL_X509* x509 = NULL; + WOLFSSL_GENERAL_NAMES* gns = NULL; + WOLFSSL_GENERAL_NAME* gn = NULL; + WOLFSSL_ASN1_STRING* dnsStr = NULL; + const char dns[] = "example.com"; + + ExpectNotNull(x509 = wolfSSL_X509_new()); + ExpectNotNull(gn = wolfSSL_GENERAL_NAME_new()); + ExpectNotNull(dnsStr = wolfSSL_ASN1_STRING_new()); + ExpectIntEQ(wolfSSL_ASN1_STRING_set(dnsStr, dns, (int)XSTRLEN(dns)), 1); + if (gn != NULL) { + wolfSSL_GENERAL_NAME_set0_value(gn, GEN_DNS, dnsStr); + dnsStr = NULL; + } + ExpectNotNull(gns = wolfSSL_sk_GENERAL_NAME_new(NULL)); + ExpectIntEQ(wolfSSL_sk_GENERAL_NAME_push(gns, gn), 1); + if (EXPECT_FAIL() && gn != NULL) { + wolfSSL_GENERAL_NAME_free(gn); + } + + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(NULL, NID_subject_alt_name, gns, 0, + 0), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, NULL, 0, + 0), WOLFSSL_FAILURE); + + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, gns, 0, + 0), WOLFSSL_SUCCESS); + + { + WOLFSSL_GENERAL_NAMES* readBack = NULL; + WOLFSSL_GENERAL_NAME* rbGn = NULL; + + ExpectNotNull(readBack = (WOLFSSL_GENERAL_NAMES*) + wolfSSL_X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, + NULL)); + ExpectIntEQ(wolfSSL_sk_GENERAL_NAME_num(readBack), 1); + ExpectNotNull(rbGn = wolfSSL_sk_GENERAL_NAME_value(readBack, 0)); + if (rbGn != NULL) { + ExpectIntEQ(rbGn->type, GEN_DNS); + ExpectNotNull(rbGn->d.dNSName); + if (rbGn->d.dNSName != NULL) { + ExpectIntEQ(rbGn->d.dNSName->length, (int)XSTRLEN(dns)); + ExpectIntEQ(XMEMCMP(rbGn->d.dNSName->data, dns, + XSTRLEN(dns)), 0); + } + } + wolfSSL_sk_GENERAL_NAME_pop_free(readBack, wolfSSL_GENERAL_NAME_free); + } + + wolfSSL_sk_GENERAL_NAME_pop_free(gns, wolfSSL_GENERAL_NAME_free); + wolfSSL_ASN1_STRING_free(dnsStr); + wolfSSL_X509_free(x509); +#endif + return EXPECT_RESULT(); +} + +#if defined(OPENSSL_EXTRA) && defined(OPENSSL_ALL) && !defined(NO_CERTS) && \ + !defined(NO_ASN) +/* Build a STACK_OF(GENERAL_NAME) holding a single GEN_DNS entry. Returns the + * stack (caller frees with wolfSSL_sk_GENERAL_NAME_pop_free) or NULL. */ +static WOLFSSL_GENERAL_NAMES* test_san_dns_stack(const char* dns) +{ + WOLFSSL_GENERAL_NAMES* gns = NULL; + WOLFSSL_GENERAL_NAME* gn = NULL; + WOLFSSL_ASN1_STRING* dnsStr = NULL; + + gn = wolfSSL_GENERAL_NAME_new(); + dnsStr = wolfSSL_ASN1_STRING_new(); + if ((gn == NULL) || (dnsStr == NULL)) { + wolfSSL_GENERAL_NAME_free(gn); + wolfSSL_ASN1_STRING_free(dnsStr); + return NULL; + } + if (wolfSSL_ASN1_STRING_set(dnsStr, dns, (int)XSTRLEN(dns)) != 1) { + wolfSSL_GENERAL_NAME_free(gn); + wolfSSL_ASN1_STRING_free(dnsStr); + return NULL; + } + /* set0 takes ownership of dnsStr. */ + wolfSSL_GENERAL_NAME_set0_value(gn, GEN_DNS, dnsStr); + + gns = wolfSSL_sk_GENERAL_NAME_new(NULL); + if (gns == NULL) { + wolfSSL_GENERAL_NAME_free(gn); + return NULL; + } + if (wolfSSL_sk_GENERAL_NAME_push(gns, gn) != 1) { + wolfSSL_GENERAL_NAME_free(gn); + wolfSSL_sk_GENERAL_NAME_pop_free(gns, wolfSSL_GENERAL_NAME_free); + return NULL; + } + return gns; +} + +/* Return the first SAN DNS string of @x509 in @out (len in @outLen), or set + * *out to NULL when no SAN is present. Returns the SAN entry count. */ +static int test_san_first_dns(WOLFSSL_X509* x509, const char** out, int* outLen) +{ + WOLFSSL_GENERAL_NAMES* sk = NULL; + WOLFSSL_GENERAL_NAME* gn = NULL; + int num = 0; + + *out = NULL; + *outLen = 0; + sk = (WOLFSSL_GENERAL_NAMES*)wolfSSL_X509_get_ext_d2i(x509, + NID_subject_alt_name, NULL, NULL); + if (sk == NULL) { + return 0; + } + num = wolfSSL_sk_GENERAL_NAME_num(sk); + gn = wolfSSL_sk_GENERAL_NAME_value(sk, 0); + if ((gn != NULL) && (gn->type == GEN_DNS) && (gn->d.dNSName != NULL)) { + /* Copy into a static buffer: the stack (and the dNSName data it owns) + * is freed below, so the caller must not see a dangling pointer. */ + static char dnsBuf[256]; + int len = gn->d.dNSName->length; + if (len > (int)sizeof(dnsBuf) - 1) { + len = (int)sizeof(dnsBuf) - 1; + } + if (len > 0) { + XMEMCPY(dnsBuf, gn->d.dNSName->data, (size_t)len); + } + dnsBuf[len] = '\0'; + *out = dnsBuf; + /* The copy, not the original: dnsBuf is what the caller sees. */ + *outLen = len; + } + wolfSSL_sk_GENERAL_NAME_pop_free(sk, wolfSSL_GENERAL_NAME_free); + return num; +} +#endif + +/* Exercise the X509V3_ADD_* operation flags of wolfSSL_X509_add1_ext_i2d() + * using the subjectAltName extension. */ +static int test_wolfSSL_X509_add1_ext_i2d_flags(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(OPENSSL_ALL) && !defined(NO_CERTS) && \ + !defined(NO_ASN) + WOLFSSL_X509* x509 = NULL; + WOLFSSL_GENERAL_NAMES* gns = NULL; + int num = 0; + + ExpectNotNull(x509 = wolfSSL_X509_new()); + + /* DELETE / REPLACE_EXISTING on an empty cert must fail. */ + ExpectNotNull(gns = test_san_dns_stack("a.example")); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, gns, 0, + X509V3_ADD_DELETE), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, gns, 0, + X509V3_ADD_REPLACE_EXISTING), WOLFSSL_FAILURE); + + /* Unknown operation must fail. */ + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, gns, 0, + X509V3_ADD_OP_MASK), WOLFSSL_FAILURE); + + /* DEFAULT adds when absent. */ + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, gns, 0, + X509V3_ADD_DEFAULT), WOLFSSL_SUCCESS); + wolfSSL_sk_GENERAL_NAME_pop_free(gns, wolfSSL_GENERAL_NAME_free); + gns = NULL; + + /* DEFAULT on an existing extension fails. */ + ExpectNotNull(gns = test_san_dns_stack("b.example")); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, gns, 0, + X509V3_ADD_DEFAULT), WOLFSSL_FAILURE); + /* SILENT is inert: still a failure, still no change. */ + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, gns, 0, + X509V3_ADD_DEFAULT | X509V3_ADD_SILENT), WOLFSSL_FAILURE); + { + const char* dnsName = NULL; + int dnsLen = 0; + num = test_san_first_dns(x509, &dnsName, &dnsLen); + ExpectIntEQ(num, 1); /* unchanged: a.example */ + } + /* APPEND adds a second value without checking for an existing one. */ + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, gns, 0, + X509V3_ADD_APPEND), WOLFSSL_SUCCESS); + { + const char* dnsName = NULL; + int dnsLen = 0; + num = test_san_first_dns(x509, &dnsName, &dnsLen); + ExpectIntEQ(num, 2); /* a.example + b.example */ + } + wolfSSL_sk_GENERAL_NAME_pop_free(gns, wolfSSL_GENERAL_NAME_free); + gns = NULL; + + /* KEEP_EXISTING is a no-op when an extension is already present. */ + ExpectNotNull(gns = test_san_dns_stack("c.example")); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, gns, 0, + X509V3_ADD_KEEP_EXISTING), WOLFSSL_SUCCESS); + { + const char* dnsName = NULL; + int dnsLen = 0; + num = test_san_first_dns(x509, &dnsName, &dnsLen); + ExpectIntEQ(num, 2); /* unchanged */ + } + wolfSSL_sk_GENERAL_NAME_pop_free(gns, wolfSSL_GENERAL_NAME_free); + gns = NULL; + + /* REPLACE clears the existing extension and adds the new value. */ + ExpectNotNull(gns = test_san_dns_stack("d.example")); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, gns, 0, + X509V3_ADD_REPLACE), WOLFSSL_SUCCESS); + { + const char* dnsName = NULL; + int dnsLen = 0; + num = test_san_first_dns(x509, &dnsName, &dnsLen); + ExpectIntEQ(num, 1); + ExpectNotNull(dnsName); + if (dnsName != NULL) { + ExpectIntEQ(dnsLen, (int)XSTRLEN("d.example")); + ExpectIntEQ(XMEMCMP(dnsName, "d.example", XSTRLEN("d.example")), 0); + } + } + wolfSSL_sk_GENERAL_NAME_pop_free(gns, wolfSSL_GENERAL_NAME_free); + gns = NULL; + + /* REPLACE_EXISTING now succeeds because the extension is present. */ + ExpectNotNull(gns = test_san_dns_stack("e.example")); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, gns, 0, + X509V3_ADD_REPLACE_EXISTING), WOLFSSL_SUCCESS); + wolfSSL_sk_GENERAL_NAME_pop_free(gns, wolfSSL_GENERAL_NAME_free); + gns = NULL; + + /* A REPLACE that cannot build the new extension must leave the existing + * one untouched. */ + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, NULL, 0, + X509V3_ADD_REPLACE), WOLFSSL_FAILURE); + { + const char* dnsName = NULL; + int dnsLen = 0; + num = test_san_first_dns(x509, &dnsName, &dnsLen); + ExpectIntEQ(num, 1); + ExpectNotNull(dnsName); + if (dnsName != NULL) { + ExpectIntEQ(dnsLen, (int)XSTRLEN("e.example")); + ExpectIntEQ(XMEMCMP(dnsName, "e.example", XSTRLEN("e.example")), 0); + } + } + + /* DELETE removes the extension; afterward get_ext_d2i finds nothing. */ + ExpectNotNull(gns = test_san_dns_stack("f.example")); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, gns, 0, + X509V3_ADD_DELETE), WOLFSSL_SUCCESS); + { + const char* dnsName = NULL; + int dnsLen = 0; + num = test_san_first_dns(x509, &dnsName, &dnsLen); + ExpectIntEQ(num, 0); + } + wolfSSL_sk_GENERAL_NAME_pop_free(gns, wolfSSL_GENERAL_NAME_free); + gns = NULL; + + wolfSSL_X509_free(x509); +#endif + return EXPECT_RESULT(); +} + +/* Same operations against a certificate parsed from file: removing an + * extension must clear every piece of state the accessors look at and must + * not leave pointers into freed memory. */ +static int test_wolfSSL_X509_add1_ext_i2d_parsed_cert(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(OPENSSL_ALL) && !defined(NO_CERTS) && \ + !defined(NO_ASN) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) + WOLFSSL_X509* x509 = NULL; + WOLFSSL_GENERAL_NAMES* gns = NULL; + WOLFSSL_STACK* eku = NULL; + const char* dnsName = NULL; + int dnsLen = 0; + + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(svrCertFile, + WOLFSSL_FILETYPE_PEM)); + + /* The parsed cert has a SAN and the altName iterator is primed with it. */ + ExpectNotNull(wolfSSL_X509_get_next_altname(x509)); + + /* REPLACE frees the parsed altName list. The iterator hint must not be + * left pointing into it. */ + ExpectNotNull(gns = test_san_dns_stack("new.example")); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_alt_name, gns, 0, + X509V3_ADD_REPLACE), WOLFSSL_SUCCESS); + ExpectNull(wolfSSL_X509_get_next_altname(x509)); + ExpectIntEQ(test_san_first_dns(x509, &dnsName, &dnsLen), 1); + ExpectNotNull(dnsName); + if (dnsName != NULL) { + ExpectIntEQ(dnsLen, (int)XSTRLEN("new.example")); + ExpectIntEQ(XMEMCMP(dnsName, "new.example", XSTRLEN("new.example")), + 0); + } + wolfSSL_sk_GENERAL_NAME_pop_free(gns, wolfSSL_GENERAL_NAME_free); + gns = NULL; + + /* DELETE of the subject key identifier drops the cached ASN1_STRING. */ + ExpectNotNull(wolfSSL_X509_get0_subject_key_id(x509)); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_key_identifier, + NULL, 0, X509V3_ADD_DELETE), WOLFSSL_SUCCESS); + ExpectNull(wolfSSL_X509_get0_subject_key_id(x509)); + /* Nothing left to delete the second time around. */ + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_subject_key_identifier, + NULL, 0, X509V3_ADD_DELETE), WOLFSSL_FAILURE); + + /* DELETE of extended key usage drops the encoded object list. */ + ExpectNotNull(eku = (WOLFSSL_STACK*)wolfSSL_X509_get_ext_d2i(x509, + NID_ext_key_usage, NULL, NULL)); + wolfSSL_sk_ASN1_OBJECT_pop_free(eku, NULL); + eku = NULL; + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_ext_key_usage, NULL, 0, + X509V3_ADD_DELETE), WOLFSSL_SUCCESS); + ExpectNull(eku = (WOLFSSL_STACK*)wolfSSL_X509_get_ext_d2i(x509, + NID_ext_key_usage, NULL, NULL)); + wolfSSL_sk_ASN1_OBJECT_pop_free(eku, NULL); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_ext_key_usage, NULL, 0, + X509V3_ADD_DELETE), WOLFSSL_FAILURE); + + /* DELETE of the authority key identifier is not observable either. */ + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_authority_key_identifier, + NULL, 0, X509V3_ADD_DELETE), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_authority_key_identifier, + NULL, 0, X509V3_ADD_DELETE), WOLFSSL_FAILURE); + + wolfSSL_X509_free(x509); +#endif + return EXPECT_RESULT(); +} + +/* basicConstraints must round trip through wolfSSL_X509_add1_ext_i2d() and be + * consumed by wolfSSL_X509_add_ext() (previously rejected as type 0). */ +static int test_wolfSSL_X509_add1_ext_i2d_basic_constraints(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(OPENSSL_ALL) && !defined(NO_CERTS) && \ + !defined(NO_ASN) + WOLFSSL_X509* x509 = NULL; + WOLFSSL_BASIC_CONSTRAINTS* bc = NULL; + WOLFSSL_BASIC_CONSTRAINTS* rb = NULL; + + ExpectNotNull(x509 = wolfSSL_X509_new()); + ExpectNotNull(bc = wolfSSL_BASIC_CONSTRAINTS_new()); + if (bc != NULL) { + bc->ca = 1; + ExpectNotNull(bc->pathlen = wolfSSL_ASN1_INTEGER_new()); + ExpectIntEQ(wolfSSL_ASN1_INTEGER_set(bc->pathlen, 3), 1); + } + + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_basic_constraints, bc, 1, + X509V3_ADD_DEFAULT), WOLFSSL_SUCCESS); + + /* Verify it was actually applied to the in-memory cert. */ + ExpectIntEQ(wolfSSL_X509_get_isCA(x509), 1); + + ExpectNotNull(rb = (WOLFSSL_BASIC_CONSTRAINTS*)wolfSSL_X509_get_ext_d2i( + x509, NID_basic_constraints, NULL, NULL)); + if (rb != NULL) { + ExpectIntEQ(rb->ca, 1); + wolfSSL_BASIC_CONSTRAINTS_free(rb); + } + + wolfSSL_BASIC_CONSTRAINTS_free(bc); + wolfSSL_X509_free(x509); +#endif + return EXPECT_RESULT(); +} + +/* extKeyUsage is represented as a stack of ASN1_OBJECTs. Adding it has to set + * both the usage bitmask and the encoded KeyPurposeId list, and OIDs wolfSSL + * has no bit for must survive in that list. */ +static int test_wolfSSL_X509_add1_ext_i2d_eku(void) +{ + EXPECT_DECLS; +/* OBJ_txt2obj() needs WOLFSSL_CERT_EXT and WOLFSSL_CERT_GEN. */ +#if defined(OPENSSL_EXTRA) && defined(OPENSSL_ALL) && !defined(NO_CERTS) && \ + !defined(NO_ASN) && defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_CERT_GEN) + /* serverAuth, clientAuth and an OID wolfSSL does not track. */ + static const char* oids[] = { "1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2", + "1.3.6.1.4.1.311.10.3.4" }; + WOLFSSL_X509* x509 = NULL; + WOLFSSL_STACK* sk = NULL; + WOLFSSL_STACK* rb = NULL; + WOLFSSL_ASN1_OBJECT* obj = NULL; + size_t i; + + ExpectNotNull(x509 = wolfSSL_X509_new()); + ExpectNotNull(sk = wolfSSL_sk_new_asn1_obj()); + for (i = 0; i < XELEM_CNT(oids); i++) { + ExpectNotNull(obj = wolfSSL_OBJ_txt2obj(oids[i], 1)); + /* push returns the new element count. */ + ExpectIntEQ(wolfSSL_sk_ASN1_OBJECT_push(sk, obj), (int)i + 1); + /* Stack does not own obj on push failure. */ + if (EXPECT_FAIL()) { + wolfSSL_ASN1_OBJECT_free(obj); + } + obj = NULL; + } + + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_ext_key_usage, sk, 0, + X509V3_ADD_DEFAULT), WOLFSSL_SUCCESS); + + /* Only the two recognized purposes set a bit. */ + ExpectIntEQ(wolfSSL_X509_get_extended_key_usage(x509), + XKU_SSL_SERVER | XKU_SSL_CLIENT); + + /* The whole list, unrecognized OID included, reads back. */ + ExpectNotNull(rb = (WOLFSSL_STACK*)wolfSSL_X509_get_ext_d2i(x509, + NID_ext_key_usage, NULL, NULL)); + ExpectIntEQ(wolfSSL_sk_num(rb), (int)XELEM_CNT(oids)); + for (i = 0; i < XELEM_CNT(oids); i++) { + char txt[80]; + + ExpectNotNull(obj = (WOLFSSL_ASN1_OBJECT*)wolfSSL_sk_value(rb, + (int)i)); + ExpectIntGT(wolfSSL_OBJ_obj2txt(txt, (int)sizeof(txt), obj, 1), 0); + ExpectStrEQ(txt, oids[i]); + obj = NULL; + } + wolfSSL_sk_ASN1_OBJECT_pop_free(rb, NULL); + rb = NULL; + + /* Now that it is present, DEFAULT fails and DELETE clears it. */ + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_ext_key_usage, sk, 0, + X509V3_ADD_DEFAULT), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_ext_key_usage, NULL, 0, + X509V3_ADD_DELETE), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_get_extended_key_usage(x509), 0); + ExpectNull(rb = (WOLFSSL_STACK*)wolfSSL_X509_get_ext_d2i(x509, + NID_ext_key_usage, NULL, NULL)); + wolfSSL_sk_ASN1_OBJECT_pop_free(rb, NULL); + + wolfSSL_sk_ASN1_OBJECT_pop_free(sk, NULL); + wolfSSL_X509_free(x509); +#endif + return EXPECT_RESULT(); +} + +/* The stack X509_get_ext_d2i() returns for extKeyUsage has to be usable as + * input to X509_add1_ext_i2d() on another certificate. */ +static int test_wolfSSL_X509_add1_ext_i2d_eku_copy(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(OPENSSL_ALL) && !defined(NO_CERTS) && \ + !defined(NO_ASN) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) + WOLFSSL_X509* src = NULL; + WOLFSSL_X509* dst = NULL; + WOLFSSL_STACK* eku = NULL; + WOLFSSL_STACK* rb = NULL; + + ExpectNotNull(src = wolfSSL_X509_load_certificate_file(svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(eku = (WOLFSSL_STACK*)wolfSSL_X509_get_ext_d2i(src, + NID_ext_key_usage, NULL, NULL)); + + ExpectNotNull(dst = wolfSSL_X509_new()); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(dst, NID_ext_key_usage, eku, 0, + X509V3_ADD_DEFAULT), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_get_extended_key_usage(dst), + wolfSSL_X509_get_extended_key_usage(src)); + + ExpectNotNull(rb = (WOLFSSL_STACK*)wolfSSL_X509_get_ext_d2i(dst, + NID_ext_key_usage, NULL, NULL)); + ExpectIntEQ(wolfSSL_sk_num(rb), wolfSSL_sk_num(eku)); + wolfSSL_sk_ASN1_OBJECT_pop_free(rb, NULL); + + wolfSSL_sk_ASN1_OBJECT_pop_free(eku, NULL); + wolfSSL_X509_free(dst); + wolfSSL_X509_free(src); +#endif + return EXPECT_RESULT(); +} + +/* A REPLACE carrying an extKeyUsage stack that cannot be stored must leave the + * certificate's existing extKeyUsage alone. */ +static int test_wolfSSL_X509_add1_ext_i2d_eku_bad_replace(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(OPENSSL_ALL) && !defined(NO_CERTS) && \ + !defined(NO_ASN) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) + WOLFSSL_X509* x509 = NULL; + WOLFSSL_STACK* sk = NULL; + WOLFSSL_STACK* rb = NULL; + WOLFSSL_ASN1_OBJECT* obj = NULL; + unsigned int eku = 0; + + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectIntGT(eku = wolfSSL_X509_get_extended_key_usage(x509), 0); + + /* A bare object carries no OID encoding. */ + ExpectNotNull(sk = wolfSSL_sk_new_asn1_obj()); + ExpectNotNull(obj = wolfSSL_ASN1_OBJECT_new()); + ExpectIntEQ(wolfSSL_sk_ASN1_OBJECT_push(sk, obj), 1); + if (EXPECT_FAIL()) { + wolfSSL_ASN1_OBJECT_free(obj); + } + + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_ext_key_usage, sk, 0, + X509V3_ADD_REPLACE), WOLFSSL_FAILURE); + + /* The extKeyUsage the certificate came with is untouched. */ + ExpectIntEQ(wolfSSL_X509_get_extended_key_usage(x509), eku); + ExpectNotNull(rb = (WOLFSSL_STACK*)wolfSSL_X509_get_ext_d2i(x509, + NID_ext_key_usage, NULL, NULL)); + wolfSSL_sk_ASN1_OBJECT_pop_free(rb, NULL); + + wolfSSL_sk_ASN1_OBJECT_pop_free(sk, NULL); + wolfSSL_X509_free(x509); +#endif + return EXPECT_RESULT(); +} + +/* wolfSSL_X509V3_EXT_i2d() builds the authorityKeyIdentifier object from the + * caller's issuer name when no key ID is given, so the encoded extension can + * be a different NID than the one asked for. That must be refused before the + * existing extension is removed. */ +static int test_wolfSSL_X509_add1_ext_i2d_nid_mismatch(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(OPENSSL_ALL) && !defined(NO_CERTS) && \ + !defined(NO_ASN) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) + WOLFSSL_X509* x509 = NULL; + WOLFSSL_AUTHORITY_KEYID* akey = NULL; + + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(svrCertFile, + WOLFSSL_FILETYPE_PEM)); + + ExpectNotNull(akey = wolfSSL_AUTHORITY_KEYID_new()); + if (akey != NULL) { + /* No key ID, so the extension carries this object instead. */ + ExpectNotNull(akey->issuer = wolfSSL_OBJ_nid2obj(NID_commonName)); + } + + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_authority_key_identifier, + akey, 0, X509V3_ADD_REPLACE), WOLFSSL_FAILURE); + /* The authority key identifier the certificate came with is still set. */ + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_authority_key_identifier, + NULL, 0, X509V3_ADD_DELETE), WOLFSSL_SUCCESS); + + wolfSSL_AUTHORITY_KEYID_free(akey); + wolfSSL_X509_free(x509); +#endif + return EXPECT_RESULT(); +} + +/* wolfSSL has no typed storage for some of the extensions + * wolfSSL_X509V3_EXT_i2d() can encode. Presence cannot be determined for + * those, so every operation but APPEND has to fail up front instead of + * quietly doing the wrong thing. */ +static int test_wolfSSL_X509_add1_ext_i2d_untracked_nid(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(OPENSSL_ALL) && !defined(NO_CERTS) && \ + !defined(NO_ASN) + WOLFSSL_X509* x509 = NULL; + WOLFSSL_GENERAL_NAMES* gns = NULL; + + ExpectNotNull(x509 = wolfSSL_X509_new()); + ExpectNotNull(gns = test_san_dns_stack("a.example")); + + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_issuer_alt_name, gns, 0, + X509V3_ADD_DEFAULT), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_issuer_alt_name, gns, 0, + X509V3_ADD_REPLACE), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_issuer_alt_name, gns, 0, + X509V3_ADD_REPLACE_EXISTING), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_issuer_alt_name, gns, 0, + X509V3_ADD_KEEP_EXISTING), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_issuer_alt_name, NULL, 0, + X509V3_ADD_DELETE), WOLFSSL_FAILURE); + /* APPEND is attempted; the store step is what rejects it. */ + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_issuer_alt_name, gns, 0, + X509V3_ADD_APPEND), WOLFSSL_FAILURE); + + /* A NID with no typed storage must not report "nothing to delete". */ + ExpectIntEQ(wolfSSL_X509_add1_ext_i2d(x509, NID_info_access, NULL, 0, + X509V3_ADD_DELETE), WOLFSSL_FAILURE); + + wolfSSL_sk_GENERAL_NAME_pop_free(gns, wolfSSL_GENERAL_NAME_free); + wolfSSL_X509_free(x509); +#endif + return EXPECT_RESULT(); +} + /* Round trip test for wolfSSL_X509_set_authority_key_id() with a raw key ID. * * Builds a cert, calls the setter with a 20-byte raw keyId, signs the cert, @@ -23968,6 +24613,80 @@ static int test_wolfSSL_d2i_and_i2d_PublicKey_ecc(void) return EXPECT_RESULT(); } +/* Round-trip test for EC_KEY_oct2key with a P-256 public point. The point + * conversion form has to follow the encoding that was decoded. */ +static int test_wolfSSL_EC_KEY_oct2key(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_ECC) && !defined(NO_ASN) + EC_KEY* src = NULL; + EC_KEY* dst = NULL; + const EC_GROUP* group = NULL; + const EC_POINT* src_pub = NULL; + const EC_POINT* dst_pub = NULL; + unsigned char buf[1 + 2 * MAX_ECC_BYTES]; + size_t enc_len = 0; + int fieldSz = 0; + + ExpectNotNull(src = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + ExpectIntEQ(EC_KEY_generate_key(src), 1); + ExpectNotNull(group = EC_KEY_get0_group(src)); + ExpectNotNull(src_pub = EC_KEY_get0_public_key(src)); + ExpectIntGT(fieldSz = (EC_GROUP_get_degree(group) + 7) / 8, 0); + + enc_len = EC_POINT_point2oct(group, src_pub, + POINT_CONVERSION_UNCOMPRESSED, buf, sizeof(buf), NULL); + ExpectIntEQ((int)enc_len, 1 + 2 * fieldSz); + + ExpectNotNull(dst = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + + ExpectIntEQ(EC_KEY_oct2key(NULL, buf, enc_len, NULL), 0); + ExpectIntEQ(EC_KEY_oct2key(dst, NULL, enc_len, NULL), 0); + ExpectIntEQ(EC_KEY_oct2key(dst, buf, 0, NULL), 0); + + ExpectIntEQ(EC_KEY_oct2key(dst, buf, enc_len, NULL), 1); + + ExpectNotNull(dst_pub = EC_KEY_get0_public_key(dst)); + ExpectIntEQ(EC_POINT_cmp(group, src_pub, dst_pub, NULL), 0); + ExpectIntEQ(EC_KEY_get_conv_form(dst), POINT_CONVERSION_UNCOMPRESSED); + + /* Point compression is what makes the recorded form observable. */ +#if defined(HAVE_COMP_KEY) && !defined(HAVE_SELFTEST) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION_GT(2,0)) + { + unsigned char comp[1 + MAX_ECC_BYTES]; + unsigned char* out = NULL; + size_t comp_len; + + comp_len = EC_POINT_point2oct(group, src_pub, + POINT_CONVERSION_COMPRESSED, comp, sizeof(comp), NULL); + ExpectIntEQ((int)comp_len, 1 + fieldSz); + + ExpectIntEQ(EC_KEY_oct2key(dst, comp, comp_len, NULL), 1); + ExpectNotNull(dst_pub = EC_KEY_get0_public_key(dst)); + ExpectIntEQ(EC_POINT_cmp(group, src_pub, dst_pub, NULL), 0); + ExpectIntEQ(EC_KEY_get_conv_form(dst), POINT_CONVERSION_COMPRESSED); + + /* i2o has to re-emit the encoding that was decoded. */ + ExpectIntEQ(i2o_ECPublicKey(dst, &out), (int)comp_len); + ExpectNotNull(out); + if (out != NULL) { + ExpectBufEQ(out, comp, comp_len); + XFREE(out, NULL, DYNAMIC_TYPE_OPENSSL); + } + + /* An uncompressed point switches the form back. */ + ExpectIntEQ(EC_KEY_oct2key(dst, buf, enc_len, NULL), 1); + ExpectIntEQ(EC_KEY_get_conv_form(dst), POINT_CONVERSION_UNCOMPRESSED); + } +#endif + + EC_KEY_free(dst); + EC_KEY_free(src); +#endif + return EXPECT_RESULT(); +} + static int test_wolfSSL_d2i_and_i2d_DSAparams(void) { EXPECT_DECLS; @@ -40527,6 +41246,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_d2i_and_i2d_PublicKey), TEST_DECL(test_wolfSSL_d2i_and_i2d_PublicKey_ecc), + TEST_DECL(test_wolfSSL_EC_KEY_oct2key), #ifndef NO_BIO TEST_DECL(test_wolfSSL_d2i_PUBKEY), TEST_DECL(test_wolfSSL_i2d_PUBKEY_bio), @@ -40594,6 +41314,15 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_X509_ALGOR_get0), TEST_DECL(test_wolfSSL_X509_SEP), TEST_DECL(test_wolfSSL_X509_set_extensions), + TEST_DECL(test_wolfSSL_X509_add1_ext_i2d), + TEST_DECL(test_wolfSSL_X509_add1_ext_i2d_flags), + TEST_DECL(test_wolfSSL_X509_add1_ext_i2d_parsed_cert), + TEST_DECL(test_wolfSSL_X509_add1_ext_i2d_basic_constraints), + TEST_DECL(test_wolfSSL_X509_add1_ext_i2d_eku), + TEST_DECL(test_wolfSSL_X509_add1_ext_i2d_eku_copy), + TEST_DECL(test_wolfSSL_X509_add1_ext_i2d_eku_bad_replace), + TEST_DECL(test_wolfSSL_X509_add1_ext_i2d_untracked_nid), + TEST_DECL(test_wolfSSL_X509_add1_ext_i2d_nid_mismatch), TEST_DECL(test_wolfSSL_X509_set_authority_key_id_roundtrip), TEST_DECL(test_wolfSSL_X509_set_authority_key_id_ex_roundtrip), TEST_DECL(test_wolfSSL_X509_set_authority_key_id_overwrite), @@ -40687,6 +41416,7 @@ TEST_CASE testCases[] = { /* Can't memory test as tcp_connect aborts. */ TEST_DECL(test_wolfSSL_d2i_SSL_SESSION_bounds_check), TEST_DECL(test_wolfSSL_sk_GENERAL_NAME), + TEST_DECL(test_wolfSSL_sk_GENERAL_NAME_new_null), TEST_DECL(test_wolfSSL_GENERAL_NAME_print), TEST_DECL(test_wolfSSL_sk_DIST_POINT), TEST_DECL(test_wolfSSL_verify_mode), @@ -40852,6 +41582,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_set_cipher_list_tls12_with_version), TEST_DECL(test_wolfSSL_set_cipher_list_tls13_with_version), TEST_DECL(test_wolfSSL_set_cipher_list_exclusions), + TEST_DECL(test_wolfSSL_SSL_set_ciphersuites), TEST_DECL(test_wolfSSL_set_alpn_protos_default_fails), TEST_DECL(test_wolfSSL_CTX_use_certificate), TEST_DECL(test_wolfSSL_CTX_use_certificate_file), diff --git a/wolfssl/openssl/ec.h b/wolfssl/openssl/ec.h index c706761e089..95e32972fa4 100644 --- a/wolfssl/openssl/ec.h +++ b/wolfssl/openssl/ec.h @@ -295,6 +295,9 @@ void wolfSSL_EC_KEY_set_asn1_flag(WOLFSSL_EC_KEY *key, int asn1_flag); WOLFSSL_API int wolfSSL_EC_KEY_set_public_key(WOLFSSL_EC_KEY *key, const WOLFSSL_EC_POINT *pub); +WOLFSSL_API +int wolfSSL_EC_KEY_oct2key(WOLFSSL_EC_KEY *key, const unsigned char *buf, + size_t len, WOLFSSL_BN_CTX *ctx); WOLFSSL_API int wolfSSL_EC_KEY_check_key(const WOLFSSL_EC_KEY *key); #if !defined(NO_FILESYSTEM) && !defined(NO_STDIO_FILESYSTEM) WOLFSSL_API int wolfSSL_EC_KEY_print_fp(XFILE fp, WOLFSSL_EC_KEY* key, @@ -491,6 +494,7 @@ typedef WOLFSSL_EC_KEY_METHOD EC_KEY_METHOD; #define ECPoint_d2i wolfSSL_ECPoint_d2i #define EC_POINT_point2oct wolfSSL_EC_POINT_point2oct #define EC_POINT_oct2point wolfSSL_EC_POINT_oct2point +#define EC_KEY_oct2key wolfSSL_EC_KEY_oct2key #define EC_POINT_point2bn wolfSSL_EC_POINT_point2bn #define EC_POINT_is_on_curve wolfSSL_EC_POINT_is_on_curve #define o2i_ECPublicKey wolfSSL_o2i_ECPublicKey diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 91d61cfdaf2..3d1a693ac61 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -402,6 +402,7 @@ typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS; #define SSL_CTX_set_cipher_list wolfSSL_CTX_set_cipher_list #define SSL_CTX_set_ciphersuites wolfSSL_CTX_set_cipher_list #define SSL_set_cipher_list wolfSSL_set_cipher_list +#define SSL_set_ciphersuites wolfSSL_set_cipher_list /* wolfSSL does not support security levels */ #define SSL_CTX_set_security_level wolfSSL_CTX_set_security_level #define SSL_CTX_get_security_level wolfSSL_CTX_get_security_level @@ -625,6 +626,7 @@ typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS; #define X509_check_issued wolfSSL_X509_check_issued #define X509_dup wolfSSL_X509_dup #define X509_add_ext wolfSSL_X509_add_ext +#define X509_add1_ext_i2d wolfSSL_X509_add1_ext_i2d #define X509_delete_ext wolfSSL_X509_delete_ext #define X509_get0_subject_key_id wolfSSL_X509_get0_subject_key_id @@ -1631,6 +1633,7 @@ typedef WOLFSSL_SRTP_PROTECTION_PROFILE SRTP_PROTECTION_PROFILE; #define SSL_SESSION_print wolfSSL_SESSION_print #define sk_GENERAL_NAME_pop_free wolfSSL_sk_GENERAL_NAME_pop_free #define sk_GENERAL_NAME_new wolfSSL_sk_GENERAL_NAME_new +#define sk_GENERAL_NAME_new_null() wolfSSL_sk_GENERAL_NAME_new(NULL) #define sk_GENERAL_NAME_free wolfSSL_sk_GENERAL_NAME_free #define sk_ASN1_OBJECT_pop_free wolfSSL_sk_ASN1_OBJECT_pop_free #define GENERAL_NAME_free wolfSSL_GENERAL_NAME_free diff --git a/wolfssl/openssl/x509v3.h b/wolfssl/openssl/x509v3.h index 480242c325b..a991bd2abe7 100644 --- a/wolfssl/openssl/x509v3.h +++ b/wolfssl/openssl/x509v3.h @@ -49,6 +49,18 @@ #define WOLFSSL_XKU_DVCS 0x80 #define WOLFSSL_XKU_ANYEKU 0x100 +/* X509V3_ADD_* operation flags for wolfSSL_X509_add1_ext_i2d(). The low nibble + * selects the operation. X509V3_ADD_SILENT is accepted but inert: this API + * reports nothing that could be suppressed. */ +#define WOLFSSL_X509V3_ADD_OP_MASK 0xfL +#define WOLFSSL_X509V3_ADD_DEFAULT 0L +#define WOLFSSL_X509V3_ADD_APPEND 1L +#define WOLFSSL_X509V3_ADD_REPLACE 2L +#define WOLFSSL_X509V3_ADD_REPLACE_EXISTING 3L +#define WOLFSSL_X509V3_ADD_KEEP_EXISTING 4L +#define WOLFSSL_X509V3_ADD_DELETE 5L +#define WOLFSSL_X509V3_ADD_SILENT 0x10L + #define WOLFSSL_X509_PURPOSE_SSL_CLIENT 0 #define WOLFSSL_X509_PURPOSE_SSL_SERVER 1 @@ -182,6 +194,15 @@ WOLFSSL_API WOLFSSL_ASN1_STRING* wolfSSL_a2i_IPADDRESS(const char* ipa); #define XKU_DVCS WOLFSSL_XKU_DVCS #define XKU_ANYEKU WOLFSSL_XKU_ANYEKU +#define X509V3_ADD_OP_MASK WOLFSSL_X509V3_ADD_OP_MASK +#define X509V3_ADD_DEFAULT WOLFSSL_X509V3_ADD_DEFAULT +#define X509V3_ADD_APPEND WOLFSSL_X509V3_ADD_APPEND +#define X509V3_ADD_REPLACE WOLFSSL_X509V3_ADD_REPLACE +#define X509V3_ADD_REPLACE_EXISTING WOLFSSL_X509V3_ADD_REPLACE_EXISTING +#define X509V3_ADD_KEEP_EXISTING WOLFSSL_X509V3_ADD_KEEP_EXISTING +#define X509V3_ADD_DELETE WOLFSSL_X509V3_ADD_DELETE +#define X509V3_ADD_SILENT WOLFSSL_X509V3_ADD_SILENT + #define X509_PURPOSE_SSL_CLIENT WOLFSSL_X509_PURPOSE_SSL_CLIENT #define X509_PURPOSE_SSL_SERVER WOLFSSL_X509_PURPOSE_SSL_SERVER diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index dd35a5f9291..66ec980995a 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -5408,6 +5408,9 @@ WOLFSSL_API int wolfSSL_X509_get_ext_by_NID(const WOLFSSL_X509 *x, int nid, int WOLFSSL_API int wolfSSL_X509_add_ext(WOLFSSL_X509 *x, WOLFSSL_X509_EXTENSION *ex, int loc); WOLFSSL_API WOLFSSL_X509_EXTENSION *wolfSSL_X509V3_EXT_i2d(int nid, int crit, void *data); +WOLFSSL_API int wolfSSL_X509_add1_ext_i2d(WOLFSSL_X509 *x, int nid, + void *value, int crit, + unsigned long flags); WOLFSSL_API WOLFSSL_X509_EXTENSION *wolfSSL_X509_delete_ext(WOLFSSL_X509 *x509, int loc); WOLFSSL_API WOLFSSL_X509_EXTENSION* wolfSSL_X509V3_EXT_conf_nid( WOLF_LHASH_OF(CONF_VALUE)* conf, WOLFSSL_X509V3_CTX* ctx, int nid,