diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 5f6057546dc..80986703b5e 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -963,6 +963,7 @@ WOLFSSL_NO_RSA_KEY_CHECK WOLFSSL_NO_SERVER_GROUPS_EXT WOLFSSL_NO_SESSION_STATS WOLFSSL_NO_SIGALG +WOLFSSL_NO_SIGNER_DER_CERT WOLFSSL_NO_SOCKADDR_UN WOLFSSL_NO_STRICT_CIPHER_SUITE WOLFSSL_NO_TICKET_EXPIRE diff --git a/src/internal.c b/src/internal.c index 3de4b1d060c..3b32215cde1 100644 --- a/src/internal.c +++ b/src/internal.c @@ -15963,59 +15963,6 @@ int DoVerifyCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl, int cert_err, if (args->certIdx > 0) { use_cb = 1; } -#endif -#if defined(OPENSSL_EXTRA) - /* Perform domain and IP check only for the leaf certificate */ - if (args->certIdx == 0) { - /* perform domain name check on the peer certificate */ - if (args->dCertInit && args->dCert && (ssl != NULL) && - ssl->param && ssl->param->hostName[0]) { - /* If altNames names is present, then subject common name is ignored */ - if (args->dCert->altNames != NULL) { - if (CheckForAltNames(args->dCert, ssl->param->hostName, - (word32)XSTRLEN(ssl->param->hostName), NULL, 0, 0) != 1) { - if (cert_err == 0) { - ret = DOMAIN_NAME_MISMATCH; - WOLFSSL_ERROR_VERBOSE(ret); - } - } - } - #ifndef WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY - else { - if (args->dCert->subjectCN) { - if (MatchDomainName( - args->dCert->subjectCN, - args->dCert->subjectCNLen, - ssl->param->hostName, - (word32)XSTRLEN(ssl->param->hostName), 0) == 0) { - if (cert_err == 0) { - ret = DOMAIN_NAME_MISMATCH; - WOLFSSL_ERROR_VERBOSE(ret); - } - } - } - } - #else - else { - if (cert_err == 0) { - ret = DOMAIN_NAME_MISMATCH; - WOLFSSL_ERROR_VERBOSE(ret); - } - } - #endif /* !WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY */ - } - - /* perform IP address check on the peer certificate */ - if ((args->dCertInit != 0) && (args->dCert != NULL) && (ssl != NULL) && - (ssl->param != NULL) && (XSTRLEN(ssl->param->ipasc) > 0)) { - if (CheckIPAddr(args->dCert, ssl->param->ipasc) != 0) { - if (cert_err == 0) { - ret = IPADDR_MISMATCH; - WOLFSSL_ERROR_VERBOSE(ret); - } - } - } - } #endif /* if verify callback has been set */ if ((use_cb && (ssl != NULL) && ((ssl->verifyCallback != NULL) @@ -18749,6 +18696,14 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, #endif domainName = (char*)ssl->buffers.domainName.buffer; + #ifdef OPENSSL_EXTRA + /* X509_VERIFY_PARAM_set1_host() is the OpenSSL way of naming + * the expected peer. */ + if ((domainName == NULL) && (ssl->param != NULL) && + (ssl->param->hostName[0] != '\0')) { + domainName = ssl->param->hostName; + } + #endif #if !defined(NO_WOLFSSL_CLIENT) && defined(HAVE_ECH) /* RFC 9849 s6.1.7: ECH offered but rejected by the server... * verify cert is valid for ECHConfig.public_name */ @@ -18823,16 +18778,29 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, #endif /* WOLFSSL_ALL_NO_CN_IN_SAN */ } -#ifndef OPENSSL_EXTRA - if (!ssl->options.verifyNone && ssl->buffers.ipasc.buffer) { - if (CheckIPAddr(args->dCert, - (const char*)ssl->buffers.ipasc.buffer) != 0) { - WOLFSSL_MSG("IPAddr match on alt names failed"); - ret = IPADDR_MISMATCH; - WOLFSSL_ERROR_VERBOSE(ret); + { + const char* ipasc = NULL; + + if (ssl->buffers.ipasc.buffer != NULL) { + ipasc = (const char*)ssl->buffers.ipasc.buffer; + } + #ifdef OPENSSL_EXTRA + /* X509_VERIFY_PARAM_set1_ip() sets the expected address + * here rather than on the WOLFSSL buffers. */ + else if ((ssl->param != NULL) && + (ssl->param->ipasc[0] != '\0')) { + ipasc = ssl->param->ipasc; + } + #endif + + if (!ssl->options.verifyNone && (ipasc != NULL)) { + if (CheckIPAddr(args->dCert, ipasc) != 0) { + WOLFSSL_MSG("IPAddr match on alt names failed"); + ret = IPADDR_MISMATCH; + WOLFSSL_ERROR_VERBOSE(ret); + } } } -#endif /* decode peer key */ if (ProcessPeerCertDecodeKey(ssl, args, &ret)) diff --git a/src/ssl.c b/src/ssl.c index d7cb7bb4c96..6d43cb8d8b4 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -4830,13 +4830,43 @@ int wolfSSL_get_peer_tmp_key(const WOLFSSL* ssl, WOLFSSL_EVP_PKEY** pkey) } #endif - *pkey = ret; -#ifdef HAVE_ECC - if (ret != NULL) - return WOLFSSL_SUCCESS; - else +#ifdef HAVE_CURVE25519 + if ((ret == NULL) && (ssl->peerX25519Key != NULL) && + ssl->peerX25519KeyPresent) { + byte pub[CURVE25519_PUB_KEY_SIZE]; + word32 pubSz = (word32)sizeof(pub); + + /* Raw X25519 keys are little-endian (RFC 7748). */ + if (wc_curve25519_export_public_ex(ssl->peerX25519Key, pub, &pubSz, + EC25519_LITTLE_ENDIAN) != 0) { + WOLFSSL_MSG("get curve25519 public key failed"); + return WOLFSSL_FAILURE; + } + ret = wolfSSL_EVP_PKEY_new_raw_public_key(WC_EVP_PKEY_X25519, NULL, + pub, (size_t)pubSz); + } #endif - return WOLFSSL_FAILURE; + +#ifdef HAVE_CURVE448 + if ((ret == NULL) && (ssl->peerX448Key != NULL) && + ssl->peerX448KeyPresent) { + byte pub[CURVE448_PUB_KEY_SIZE]; + word32 pubSz = (word32)sizeof(pub); + + /* Raw X448 keys are little-endian (RFC 7748). */ + if (wc_curve448_export_public_ex(ssl->peerX448Key, pub, &pubSz, + EC448_LITTLE_ENDIAN) != 0) { + WOLFSSL_MSG("get curve448 public key failed"); + return WOLFSSL_FAILURE; + } + ret = wolfSSL_EVP_PKEY_new_raw_public_key(WC_EVP_PKEY_X448, NULL, + pub, (size_t)pubSz); + } +#endif + + *pkey = ret; + + return (ret != NULL) ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE; } #endif /* !NO_WOLFSSL_SERVER */ @@ -9403,6 +9433,25 @@ const WOLF_EC_NIST_NAME kNistCurves[] = { #endif #endif /* WOLFSSL_MLKEM_KYBER */ #endif /* WOLFSSL_HAVE_MLKEM */ +#ifndef NO_DH + /* Finite field groups have no NID; OpenSSL identifies them by TLS group + * code point, so use that for both fields. */ + #ifdef HAVE_FFDHE_2048 + {CURVE_NAME("ffdhe2048"), WOLFSSL_FFDHE_2048, WOLFSSL_FFDHE_2048}, + #endif + #ifdef HAVE_FFDHE_3072 + {CURVE_NAME("ffdhe3072"), WOLFSSL_FFDHE_3072, WOLFSSL_FFDHE_3072}, + #endif + #ifdef HAVE_FFDHE_4096 + {CURVE_NAME("ffdhe4096"), WOLFSSL_FFDHE_4096, WOLFSSL_FFDHE_4096}, + #endif + #ifdef HAVE_FFDHE_6144 + {CURVE_NAME("ffdhe6144"), WOLFSSL_FFDHE_6144, WOLFSSL_FFDHE_6144}, + #endif + #ifdef HAVE_FFDHE_8192 + {CURVE_NAME("ffdhe8192"), WOLFSSL_FFDHE_8192, WOLFSSL_FFDHE_8192}, + #endif +#endif /* !NO_DH */ #ifdef WOLFSSL_SM2 {CURVE_NAME("SM2"), WC_NID_sm2, WOLFSSL_ECC_SM2P256V1}, #endif @@ -9565,6 +9614,145 @@ int set_curves_list(WOLFSSL* ssl, WOLFSSL_CTX *ctx, const char* names, return ret; } +/* Get the group used for key exchange. + * + * Mirrors OpenSSL's SSL_get_negotiated_group(): the NID is returned for groups + * wolfSSL gives one, the IANA code point otherwise. NID values are wolfSSL's + * WC_NID_* (not every one matches OpenSSL's), so compare against those rather + * than against literals. The result can be passed to wolfSSL_group_to_name(). + * + * @param [in] ssl SSL/TLS object. + * @return Group identifier on success. + * @return 0 when ssl is NULL or no group has been negotiated. + */ +int wolfSSL_get_negotiated_group(const WOLFSSL* ssl) +{ + word16 group; + const WOLF_EC_NIST_NAME* nist_name; + + WOLFSSL_ENTER("wolfSSL_get_negotiated_group"); + + if (ssl == NULL) + return 0; + + group = ssl->namedGroup; + +#ifdef HAVE_CURVE25519 + if ((group == 0) && (ssl->ecdhCurveOID == ECC_X25519_OID)) + group = WOLFSSL_ECC_X25519; +#endif +#ifdef HAVE_CURVE448 + if ((group == 0) && (ssl->ecdhCurveOID == ECC_X448_OID)) + group = WOLFSSL_ECC_X448; +#endif +#ifdef HAVE_ECC + if ((group == 0) && (ssl->ecdhCurveOID != 0)) + group = GetCurveByOID((int)ssl->ecdhCurveOID); +#endif + + if (group == 0) + return 0; + + for (nist_name = kNistCurves; nist_name->name != NULL; nist_name++) { + if (nist_name->curve == group) + return nist_name->nid; + } + + return (int)group; +} + +/* Names OpenSSL gives the TLS supported groups. These are the TLS group + * registry spellings, which differ from both the NIST curve names in + * kNistCurves ("P-256" is "secp256r1" here) and from wolfSSL_get_curve_name(). + */ +static const struct { + word16 group; + const char* name; +} kTlsGroupNames[] = { +#ifdef HAVE_ECC + {WOLFSSL_ECC_SECP256R1, "secp256r1"}, + {WOLFSSL_ECC_SECP384R1, "secp384r1"}, + {WOLFSSL_ECC_SECP521R1, "secp521r1"}, + {WOLFSSL_ECC_SECP224R1, "secp224r1"}, + {WOLFSSL_ECC_SECP192R1, "secp192r1"}, + {WOLFSSL_ECC_SECP256K1, "secp256k1"}, +#ifdef HAVE_ECC_BRAINPOOL + {WOLFSSL_ECC_BRAINPOOLP256R1, "brainpoolP256r1"}, + {WOLFSSL_ECC_BRAINPOOLP384R1, "brainpoolP384r1"}, + {WOLFSSL_ECC_BRAINPOOLP512R1, "brainpoolP512r1"}, + {WOLFSSL_ECC_BRAINPOOLP256R1TLS13, "brainpoolP256r1tls13"}, + {WOLFSSL_ECC_BRAINPOOLP384R1TLS13, "brainpoolP384r1tls13"}, + {WOLFSSL_ECC_BRAINPOOLP512R1TLS13, "brainpoolP512r1tls13"}, +#endif +#endif /* HAVE_ECC */ +#ifdef HAVE_CURVE25519 + {WOLFSSL_ECC_X25519, "x25519"}, +#endif +#ifdef HAVE_CURVE448 + {WOLFSSL_ECC_X448, "x448"}, +#endif +#ifndef NO_DH + {WOLFSSL_FFDHE_2048, "ffdhe2048"}, + {WOLFSSL_FFDHE_3072, "ffdhe3072"}, + {WOLFSSL_FFDHE_4096, "ffdhe4096"}, + {WOLFSSL_FFDHE_6144, "ffdhe6144"}, + {WOLFSSL_FFDHE_8192, "ffdhe8192"}, +#endif +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_NO_ML_KEM) + {WOLFSSL_ML_KEM_512, "MLKEM512"}, + {WOLFSSL_ML_KEM_768, "MLKEM768"}, + {WOLFSSL_ML_KEM_1024, "MLKEM1024"}, +#if defined(HAVE_ECC) && defined(WOLFSSL_PQC_HYBRIDS) + {WOLFSSL_SECP256R1MLKEM768, "SecP256r1MLKEM768"}, + {WOLFSSL_SECP384R1MLKEM1024, "SecP384r1MLKEM1024"}, +#ifdef HAVE_CURVE25519 + {WOLFSSL_X25519MLKEM768, "X25519MLKEM768"}, +#endif +#endif +#endif /* WOLFSSL_HAVE_MLKEM && !WOLFSSL_NO_ML_KEM */ + {0, NULL} +}; + +/* Get the name of a group. + * + * @param [in] ssl SSL/TLS object. Unused, present for OpenSSL compatibility. + * @param [in] id NID or IANA code point of the group. + * @return Name of the group on success. + * @return NULL when the group is unknown. + */ +const char* wolfSSL_group_to_name(const WOLFSSL* ssl, int id) +{ + const WOLF_EC_NIST_NAME* nist_name; + int group = id; + int i; + + WOLFSSL_ENTER("wolfSSL_group_to_name"); + + (void)ssl; + + /* wolfSSL_get_negotiated_group() reports a NID where one exists, so map + * NIDs back to their code point before looking the name up. */ + for (nist_name = kNistCurves; nist_name->name != NULL; nist_name++) { + if (nist_name->nid == id) { + group = (int)nist_name->curve; + break; + } + } + + for (i = 0; kTlsGroupNames[i].name != NULL; i++) { + if ((int)kTlsGroupNames[i].group == group) + return kTlsGroupNames[i].name; + } + + /* Not a group OpenSSL names - fall back to the wolfSSL name. */ + for (nist_name = kNistCurves; nist_name->name != NULL; nist_name++) { + if ((int)nist_name->curve == group) + return nist_name->name; + } + + return NULL; +} + #endif /* (HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448) */ #endif /* OPENSSL_EXTRA || HAVE_CURL */ diff --git a/src/tls.c b/src/tls.c index 1fe31d43af0..8809747396c 100644 --- a/src/tls.c +++ b/src/tls.c @@ -9759,8 +9759,9 @@ static int TLSX_KeyShare_ProcessX25519_ex(WOLFSSL* ssl, * falls through to the cleanup code below. */ } - /* done with key share, release resources */ - if (ssl->peerX25519Key != NULL) { + /* done with key share, release resources unless the peer key was asked + * for - wolfSSL_get_peer_tmp_key() needs it after the handshake */ + if ((ssl->peerX25519Key != NULL) && !ssl->options.keepResources) { wc_curve25519_free(ssl->peerX25519Key); XFREE(ssl->peerX25519Key, ssl->heap, DYNAMIC_TYPE_TLSX); ssl->peerX25519Key = NULL; @@ -9872,8 +9873,18 @@ static int TLSX_KeyShare_ProcessX448_ex(WOLFSSL* ssl, ssOutput, ssOutSz, EC448_LITTLE_ENDIAN); } - wc_curve448_free(peerX448Key); - XFREE(peerX448Key, ssl->heap, DYNAMIC_TYPE_TLSX); + /* Keep the peer key when it was asked for - wolfSSL_get_peer_tmp_key() + * needs it after the handshake. Freed with the other peer keys on + * teardown. */ + if ((ret == 0) && ssl->options.keepResources) { + FreeKey(ssl, DYNAMIC_TYPE_CURVE448, (void**)&ssl->peerX448Key); + ssl->peerX448Key = peerX448Key; + ssl->peerX448KeyPresent = 1; + } + else { + wc_curve448_free(peerX448Key); + XFREE(peerX448Key, ssl->heap, DYNAMIC_TYPE_TLSX); + } wc_curve448_free((curve448_key*)keyShareEntry->key); XFREE(keyShareEntry->key, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); keyShareEntry->key = NULL; @@ -10046,11 +10057,13 @@ static int TLSX_KeyShare_ProcessEcc_ex(WOLFSSL* ssl, #endif } - /* done with key share, release resources */ + /* done with key share, release resources unless the peer key was asked + * for - wolfSSL_get_peer_tmp_key() needs it after the handshake */ if (ssl->peerEccKey != NULL #ifdef HAVE_PK_CALLBACKS && ssl->ctx->EccSharedSecretCb == NULL #endif + && !ssl->options.keepResources ) { wc_ecc_free(ssl->peerEccKey); XFREE(ssl->peerEccKey, ssl->heap, DYNAMIC_TYPE_ECC); diff --git a/tests/api/test_ssl_ext.c b/tests/api/test_ssl_ext.c index 09bbfc0e0cc..d31e5460543 100644 --- a/tests/api/test_ssl_ext.c +++ b/tests/api/test_ssl_ext.c @@ -196,6 +196,20 @@ int test_wolfSSL_set1_groups_list_ext(void) #endif #endif +#if !defined(NO_DH) && defined(HAVE_FFDHE_2048) + /* Finite field groups are accepted by their OpenSSL names, on their own + * and mixed into a list of curves. */ + ExpectIntEQ(wolfSSL_CTX_set1_groups_list(ctx, "ffdhe2048"), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set1_groups_list(ssl, "P-256:ffdhe2048"), + WOLFSSL_SUCCESS); + /* set1_curves_list() is EC-only and still rejects them. */ + ExpectIntEQ(wolfSSL_CTX_set1_curves_list(ctx, "ffdhe2048"), + WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_set1_curves_list(ssl, "P-256:ffdhe2048"), + WOLFSSL_FAILURE); +#endif + wolfSSL_free(ssl); wolfSSL_CTX_free(ctx); #endif diff --git a/tests/api/test_tls.c b/tests/api/test_tls.c index ed8ab7c6c2f..d93f396f2f1 100644 --- a/tests/api/test_tls.c +++ b/tests/api/test_tls.c @@ -297,6 +297,215 @@ int test_tls_record_overflow_alert(void) return EXPECT_RESULT(); } +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \ + !defined(NO_RSA) && !defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_WOLFSSL_SERVER) + +static int test_peer_name_cb_err = 0; +static int test_peer_name_cb_preverify = -1; + +static int test_peer_name_verify_cb(int preverify, + WOLFSSL_X509_STORE_CTX* store) +{ + /* Only record the first failure - later certificates in the chain would + * otherwise overwrite it. */ + if (!preverify && (test_peer_name_cb_err == 0)) { + test_peer_name_cb_preverify = preverify; + test_peer_name_cb_err = wolfSSL_X509_STORE_CTX_get_error(store); + } + return preverify; +} + +/* mode: 0 = check_ip_address(), 1 = set1_ip_asc(), 2 = set1_host() */ +static int test_peer_name_mismatch(int mode, const char* name, int expectErr) +{ + EXPECT_DECLS; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + + test_peer_name_cb_err = 0; + test_peer_name_cb_preverify = -1; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfSSLv23_client_method, wolfSSLv23_server_method), 0); + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, test_peer_name_verify_cb); + + if (mode == 0) { + ExpectIntEQ(wolfSSL_check_ip_address(ssl_c, name), WOLFSSL_SUCCESS); + } + else if (mode == 1) { + ExpectIntEQ(wolfSSL_X509_VERIFY_PARAM_set1_ip_asc( + wolfSSL_get0_param(ssl_c), name), WOLFSSL_SUCCESS); + } + else { + ExpectIntEQ(wolfSSL_X509_VERIFY_PARAM_set1_host( + wolfSSL_get0_param(ssl_c), name, 0), WOLFSSL_SUCCESS); + } + + if (expectErr == 0) { + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(test_peer_name_cb_err, 0); + } + else { + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + /* The mismatch has to reach the application's verify callback so it + * can apply its own policy, not just fail the handshake. */ + ExpectIntEQ(test_peer_name_cb_preverify, 0); + ExpectIntEQ(test_peer_name_cb_err, expectErr); + } + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + + return EXPECT_RESULT(); +} +#endif + +/* A peer name mismatch must be reported through SSL_set_verify()'s callback, + * whichever API named the expected peer. */ +int test_tls_peer_name_mismatch_verify_cb(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \ + !defined(NO_RSA) && !defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(NO_SHA256) +#ifdef WOLFSSL_IP_ALT_NAME + /* certs/server-cert.pem carries IP:127.0.0.1 and DNS:example.com. */ + ExpectIntEQ(test_peer_name_mismatch(0, "127.0.0.2", + WC_NO_ERR_TRACE(IPADDR_MISMATCH)), TEST_SUCCESS); + ExpectIntEQ(test_peer_name_mismatch(1, "127.0.0.2", + WC_NO_ERR_TRACE(IPADDR_MISMATCH)), TEST_SUCCESS); + ExpectIntEQ(test_peer_name_mismatch(0, "127.0.0.1", 0), TEST_SUCCESS); +#endif + ExpectIntEQ(test_peer_name_mismatch(2, "wrong.example.com", + WC_NO_ERR_TRACE(DOMAIN_NAME_MISMATCH)), TEST_SUCCESS); + ExpectIntEQ(test_peer_name_mismatch(2, "example.com", 0), TEST_SUCCESS); +#endif + return EXPECT_RESULT(); +} + +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_RSA) && !defined(NO_CERTS) +static int test_peer_tmp_key_group(int group, int tls13) +{ + EXPECT_DECLS; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + WOLFSSL_EVP_PKEY* pkey = NULL; + int groups[1]; + + groups[0] = group; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + tls13 ? wolfTLSv1_3_client_method : wolfTLSv1_2_client_method, + tls13 ? wolfTLSv1_3_server_method : wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_KeepHandshakeResources(ssl_c), 0); + ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + ExpectIntEQ(wolfSSL_get_peer_tmp_key(ssl_c, &pkey), WOLFSSL_SUCCESS); + ExpectNotNull(pkey); + wolfSSL_EVP_PKEY_free(pkey); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + + return EXPECT_RESULT(); +} +#endif + +/* SSL_get_peer_tmp_key() has to return the peer's ephemeral key for every key + * exchange group, not just TLS 1.2 ECDHE. */ +int test_tls_get_peer_tmp_key(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_RSA) && !defined(NO_CERTS) && !defined(NO_SHA256) +#if defined(HAVE_ECC) && !defined(NO_ECC_SECP) && \ + (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) +#ifndef WOLFSSL_NO_TLS12 + ExpectIntEQ(test_peer_tmp_key_group(WOLFSSL_ECC_SECP256R1, 0), + TEST_SUCCESS); +#endif +#ifdef WOLFSSL_TLS13 + ExpectIntEQ(test_peer_tmp_key_group(WOLFSSL_ECC_SECP256R1, 1), + TEST_SUCCESS); +#endif +#endif /* HAVE_ECC */ +#if defined(WOLFSSL_TLS13) && defined(HAVE_CURVE25519) && !defined(HAVE_FIPS) + ExpectIntEQ(test_peer_tmp_key_group(WOLFSSL_ECC_X25519, 1), TEST_SUCCESS); +#endif +#if defined(WOLFSSL_TLS13) && defined(HAVE_CURVE448) && !defined(HAVE_FIPS) + ExpectIntEQ(test_peer_tmp_key_group(WOLFSSL_ECC_X448, 1), TEST_SUCCESS); +#endif +#endif + return EXPECT_RESULT(); +} + +/* SSL_get_negotiated_group() and SSL_group_to_name() report the group that was + * negotiated, using the names OpenSSL gives the TLS supported groups. */ +int test_tls_get_negotiated_group(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \ + defined(WOLFSSL_TLS13) && defined(HAVE_ECC) && !defined(NO_ECC_SECP) && \ + (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + int group = 0; + int p256[] = {WOLFSSL_ECC_SECP256R1}; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_set_groups(ssl_c, p256, 1), WOLFSSL_SUCCESS); + + /* Nothing negotiated yet. */ + ExpectIntEQ(wolfSSL_get_negotiated_group(NULL), 0); + ExpectIntEQ(wolfSSL_get_negotiated_group(ssl_c), 0); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + ExpectIntNE((group = wolfSSL_get_negotiated_group(ssl_c)), 0); + ExpectIntEQ(group, wolfSSL_get_negotiated_group(ssl_s)); + /* OpenSSL names this group secp256r1, not by its NIST name P-256. */ + ExpectStrEQ(wolfSSL_group_to_name(ssl_c, group), "secp256r1"); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + + /* Groups can also be named directly by their IANA code point. */ + ExpectStrEQ(wolfSSL_group_to_name(NULL, WOLFSSL_ECC_SECP256R1), + "secp256r1"); +#ifdef HAVE_CURVE25519 + ExpectStrEQ(wolfSSL_group_to_name(NULL, WOLFSSL_ECC_X25519), "x25519"); +#endif +#if !defined(NO_DH) && defined(HAVE_FFDHE_2048) + ExpectStrEQ(wolfSSL_group_to_name(NULL, WOLFSSL_FFDHE_2048), "ffdhe2048"); +#endif +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_NO_ML_KEM) && \ + !defined(WOLFSSL_NO_ML_KEM_768) + /* OpenSSL spells the standalone ML-KEM groups without underscores. */ + ExpectStrEQ(wolfSSL_group_to_name(NULL, WOLFSSL_ML_KEM_768), "MLKEM768"); +#endif + ExpectNull(wolfSSL_group_to_name(NULL, 9999)); +#endif + return EXPECT_RESULT(); +} + int test_tls12_curve_intersection(void) { EXPECT_DECLS; #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ diff --git a/tests/api/test_tls.h b/tests/api/test_tls.h index f009bb986ee..ca00e832778 100644 --- a/tests/api/test_tls.h +++ b/tests/api/test_tls.h @@ -26,6 +26,9 @@ int test_utils_memio_move_message(void); int test_tls12_unexpected_ccs(void); int test_tls13_unexpected_ccs(void); int test_tls_record_overflow_alert(void); +int test_tls_peer_name_mismatch_verify_cb(void); +int test_tls_get_peer_tmp_key(void); +int test_tls_get_negotiated_group(void); int test_tls12_curve_intersection(void); int test_tls12_dhe_rsa_pss_sigalg(void); int test_tls12_ske_sig_param_binding(void); @@ -67,6 +70,9 @@ int test_wolfSSL_get_shared_ciphers(void); TEST_DECL_GROUP("tls", test_tls12_unexpected_ccs), \ TEST_DECL_GROUP("tls", test_tls13_unexpected_ccs), \ TEST_DECL_GROUP("tls", test_tls_record_overflow_alert), \ + TEST_DECL_GROUP("tls", test_tls_peer_name_mismatch_verify_cb), \ + TEST_DECL_GROUP("tls", test_tls_get_peer_tmp_key), \ + TEST_DECL_GROUP("tls", test_tls_get_negotiated_group), \ TEST_DECL_GROUP("tls", test_tls12_curve_intersection), \ TEST_DECL_GROUP("tls", test_tls12_dhe_rsa_pss_sigalg), \ TEST_DECL_GROUP("tls", test_tls12_ske_sig_param_binding), \ diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 5c95da71316..fc69471d2f3 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -412,6 +412,9 @@ typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS; #define SSL_CTX_set1_groups_list wolfSSL_CTX_set1_groups_list #define SSL_set1_groups_list wolfSSL_set1_groups_list +#define SSL_get_negotiated_group wolfSSL_get_negotiated_group +#define SSL_group_to_name wolfSSL_group_to_name + #define SSL_set_ex_data wolfSSL_set_ex_data #define SSL_get_shutdown wolfSSL_get_shutdown #define SSL_get_finished wolfSSL_get_finished diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index c168ec24d9c..7cfbf69d5e4 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -1500,6 +1500,13 @@ WOLFSSL_API int wolfSSL_set1_groups_list(WOLFSSL *ssl, const char *list); #endif #endif +#if defined(OPENSSL_EXTRA) || defined(HAVE_CURL) +#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448) +WOLFSSL_API int wolfSSL_get_negotiated_group(const WOLFSSL* ssl); +WOLFSSL_API const char* wolfSSL_group_to_name(const WOLFSSL* ssl, int id); +#endif +#endif + #ifdef WOLFSSL_TLS13 WOLFSSL_API int wolfSSL_send_hrr_cookie(WOLFSSL* ssl, const unsigned char* secret, unsigned int secretSz); diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 696ece03230..e2abc8fb4a6 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -4939,6 +4939,14 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." #define WOLFSSL_COPY_KEY #endif +/* X509_STORE_get0_objects() can only enumerate a store's certificates when the + * CA table keeps their DER. Costs one DER copy per loaded CA, so it stays + * optional; define WOLFSSL_NO_SIGNER_DER_CERT to opt back out. */ +#if defined(OPENSSL_ALL) && !defined(WOLFSSL_NO_SIGNER_DER_CERT) + #undef WOLFSSL_SIGNER_DER_CERT + #define WOLFSSL_SIGNER_DER_CERT +#endif + /* * Keeps the "Finished" messages after a TLS handshake for use as the so-called * "tls-unique" channel binding. See comment in internal.h around clientFinished