diff --git a/src/internal.c b/src/internal.c index 2aead00269..051bb752ea 100644 --- a/src/internal.c +++ b/src/internal.c @@ -8665,6 +8665,9 @@ void FreeKeyExchange(WOLFSSL* ssl) { /* Cleanup signature buffer */ if (ssl->buffers.sig.buffer) { + /* May transiently hold the client's DH private exponent in the + * TLS 1.2 diffie_hellman_kea / dhe_psk_kea paths. */ + ForceZero(ssl->buffers.sig.buffer, ssl->buffers.sig.length); XFREE(ssl->buffers.sig.buffer, ssl->heap, DYNAMIC_TYPE_SIGNATURE); ssl->buffers.sig.buffer = NULL; ssl->buffers.sig.length = 0; diff --git a/src/pk.c b/src/pk.c index a0df3af265..7ebcdd0a3a 100644 --- a/src/pk.c +++ b/src/pk.c @@ -4743,6 +4743,7 @@ int wolfSSL_DH_generate_key(WOLFSSL_DH* dh) int ret = 1; word32 pubSz = 0; word32 privSz = 0; + word32 privAllocSz = 0; int localRng = 0; WC_RNG* rng = NULL; WC_DECLARE_VAR(tmpRng, WC_RNG, 1, 0); @@ -4792,9 +4793,12 @@ int wolfSSL_DH_generate_key(WOLFSSL_DH* dh) else { privSz = pubSz; } - /* Allocate public and private key arrays. */ + /* Allocate public and private key arrays. Preserve the allocation + * size because wc_DhGenerateKeyPair updates privSz in-place. */ + privAllocSz = privSz; pub = (unsigned char*)XMALLOC(pubSz, NULL, DYNAMIC_TYPE_PUBLIC_KEY); - priv = (unsigned char*)XMALLOC(privSz, NULL, DYNAMIC_TYPE_PRIVATE_KEY); + priv = (unsigned char*)XMALLOC(privAllocSz, NULL, + DYNAMIC_TYPE_PRIVATE_KEY); if (pub == NULL || priv == NULL) { WOLFSSL_ERROR_MSG("Unable to malloc memory"); ret = 0; @@ -4846,7 +4850,10 @@ int wolfSSL_DH_generate_key(WOLFSSL_DH* dh) } /* Dispose of allocated data. */ XFREE(pub, NULL, DYNAMIC_TYPE_PUBLIC_KEY); - XFREE(priv, NULL, DYNAMIC_TYPE_PRIVATE_KEY); + if (priv != NULL) { + ForceZero(priv, privAllocSz); + XFREE(priv, NULL, DYNAMIC_TYPE_PRIVATE_KEY); + } return ret; } diff --git a/src/sniffer.c b/src/sniffer.c index 9f63a038c7..a67094647d 100644 --- a/src/sniffer.c +++ b/src/sniffer.c @@ -2459,11 +2459,15 @@ static void FreeSetupKeysArgs(WOLFSSL* ssl, void* pArgs) args->key->type = WC_PK_TYPE_NONE; args->key->initPriv = 0; args->key->initPub = 0; + /* Scrub the raw DH private exponent (and any other key material + * embedded in the union) before release. wc_FreeDhKey above only + * clears the mp_int DhKey, not the separate privKey byte array. + * Use ForceZero (rather than XMEMSET) so the wipe cannot be + * elided by the optimizer. */ + ForceZero(args->key, sizeof(*args->key)); #ifdef WOLFSSL_ASYNC_CRYPT XFREE(args->key, NULL, DYNAMIC_TYPE_SNIFFER_KEY); args->key = NULL; -#else - XMEMSET(args->key, 0, sizeof(args->key)); #endif } diff --git a/src/ssl.c b/src/ssl.c index c6464e59a7..ed75b75a13 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -10052,11 +10052,21 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out, #ifdef WOLFSSL_DTLS ssl->options.dtlsStateful = 0; #endif + #ifdef WOLFSSL_TLS13 #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) - ssl->options.noPskDheKe = 0; - #ifdef HAVE_SUPPORTED_CURVES - ssl->options.onlyPskDheKe = 0; - #endif + if (ssl->ctx != NULL) { + ssl->options.noPskDheKe = ssl->ctx->noPskDheKe; + #ifdef HAVE_SUPPORTED_CURVES + ssl->options.onlyPskDheKe = ssl->ctx->onlyPskDheKe; + #endif + } + else { + ssl->options.noPskDheKe = 0; + #ifdef HAVE_SUPPORTED_CURVES + ssl->options.onlyPskDheKe = 0; + #endif + } + #endif #endif #ifdef HAVE_SESSION_TICKET #ifdef WOLFSSL_TLS13 diff --git a/src/ssl_crypto.c b/src/ssl_crypto.c index c5cee713ef..81f17c8dda 100644 --- a/src/ssl_crypto.c +++ b/src/ssl_crypto.c @@ -2722,11 +2722,19 @@ void wolfSSL_DES_ncbc_encrypt(const unsigned char* input, unsigned char* output, int enc) { unsigned char tmp[DES_IV_SIZE]; - /* Calculate length to a multiple of block size. */ - size_t offset = (size_t)length; + size_t offset; WOLFSSL_ENTER("wolfSSL_DES_ncbc_encrypt"); + /* Zero/negative length: no block to derive an IV from. The offset math + * below would underflow for length == 0, yielding a wild-pointer read. */ + if (length <= 0) { + WOLFSSL_LEAVE("wolfSSL_DES_ncbc_encrypt", 0); + return; + } + + /* Calculate length to a multiple of block size. */ + offset = (size_t)length; offset = (offset + DES_BLOCK_SIZE - 1) / DES_BLOCK_SIZE; offset *= DES_BLOCK_SIZE; offset -= DES_BLOCK_SIZE; diff --git a/src/ssl_load.c b/src/ssl_load.c index 04fecf2181..8a53cef0e6 100644 --- a/src/ssl_load.c +++ b/src/ssl_load.c @@ -5451,10 +5451,13 @@ int wolfSSL_CTX_set_default_verify_paths(WOLFSSL_CTX* ctx) ret = 1; } #else - /* OpenSSL's implementation of this API does not require loading the - * system CA cert directory. Allow skipping this without erroring out. - */ - ret = 1; + /* No source available: SSL_CERT_DIR/SSL_CERT_FILE not set and + * WOLFSSL_SYS_CA_CERTS not compiled in. Returning success would be + * fail-open since no trust anchors were loaded. */ + WOLFSSL_MSG("wolfSSL_CTX_set_default_verify_paths: no CA source " + "available (build without WOLFSSL_SYS_CA_CERTS and no " + "SSL_CERT_DIR/SSL_CERT_FILE env)"); + ret = WOLFSSL_FAILURE; #endif } diff --git a/src/ssl_sess.c b/src/ssl_sess.c index a2d97399f2..a14e3a4bf9 100644 --- a/src/ssl_sess.c +++ b/src/ssl_sess.c @@ -1586,8 +1586,12 @@ int wolfSSL_SetSession(WOLFSSL* ssl, WOLFSSL_SESSION* session) #if !defined(OPENSSL_EXTRA) || !defined(WOLFSSL_ERROR_CODE_OPENSSL) return WOLFSSL_FAILURE; /* session timed out */ #else /* defined(OPENSSL_EXTRA) && defined(WOLFSSL_ERROR_CODE_OPENSSL) */ + /* Return success for OpenSSL compatibility but do not carry the + * expired session's version/cipher into ssl state, which would + * otherwise pin the ClientHello to stale values. */ WOLFSSL_MSG("Session is expired but return success for " "OpenSSL compatibility"); + return WOLFSSL_SUCCESS; #endif } ssl->options.resuming = 1; diff --git a/src/tls.c b/src/tls.c index ed8267469d..e8fb643991 100644 --- a/src/tls.c +++ b/src/tls.c @@ -7592,21 +7592,28 @@ static word16 TLSX_CA_Names_GetSize(void* data) { WOLFSSL* ssl = (WOLFSSL*)data; WOLF_STACK_OF(WOLFSSL_X509_NAME)* names; - word16 size = 0; + word32 size = OPAQUE16_LEN; - /* Length of names */ - size += OPAQUE16_LEN; for (names = SSL_PRIORITY_CA_NAMES(ssl); names != NULL; names = names->next) { byte seq[MAX_SEQ_SZ]; WOLFSSL_X509_NAME* name = names->data.name; if (name != NULL) { /* 16-bit length | SEQ | Len | DER of name */ - size += (word16)(OPAQUE16_LEN + SetSequence(name->rawLen, seq) + - name->rawLen); + word32 entrySz = (word32)OPAQUE16_LEN + + (word32)SetSequence(name->rawLen, seq) + + (word32)name->rawLen; + /* Truncate at the 16-bit wire limit rather than overflowing + * a word16 and producing a heap over-write. Write() must + * mirror this truncation exactly. */ + if (size + entrySz > WOLFSSL_MAX_16BIT) { + WOLFSSL_MSG("CA Names truncated at 16-bit wire limit"); + break; + } + size += entrySz; } } - return size; + return (word16)size; } static word16 TLSX_CA_Names_Write(void* data, byte* output) @@ -7614,6 +7621,7 @@ static word16 TLSX_CA_Names_Write(void* data, byte* output) WOLFSSL* ssl = (WOLFSSL*)data; WOLF_STACK_OF(WOLFSSL_X509_NAME)* names; byte* len; + word32 written = OPAQUE16_LEN; /* Reserve space for the length value */ len = output; @@ -7623,6 +7631,14 @@ static word16 TLSX_CA_Names_Write(void* data, byte* output) WOLFSSL_X509_NAME* name = names->data.name; if (name != NULL) { + word32 entrySz = (word32)OPAQUE16_LEN + + (word32)SetSequence(name->rawLen, seq) + + (word32)name->rawLen; + /* Must match GetSize() truncation exactly. */ + if (written + entrySz > WOLFSSL_MAX_16BIT) + break; + written += entrySz; + c16toa((word16)name->rawLen + (word16)SetSequence(name->rawLen, seq), output); output += OPAQUE16_LEN; @@ -9145,6 +9161,9 @@ static void TLSX_KeyShare_FreeAll(KeyShareEntry* list, void* heap) if (WOLFSSL_NAMED_GROUP_IS_FFDHE(current->group)) { #ifndef NO_DH wc_FreeDhKey((DhKey*)current->key); + if (current->privKey != NULL && current->privKeyLen > 0) { + ForceZero(current->privKey, current->privKeyLen); + } #endif } else if (current->group == WOLFSSL_ECC_X25519) { @@ -17161,8 +17180,8 @@ static word16 TLSX_GetMinSize_Server(const word16 *type) /** Parses a buffer of TLS extensions. */ -int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType, - Suites *suites) +WOLFSSL_TEST_VIS int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, + byte msgType, Suites *suites) { int ret = 0; word16 offset = 0; @@ -17770,6 +17789,20 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType, #ifdef WOLFSSL_SRTP case TLSX_USE_SRTP: WOLFSSL_MSG("Use SRTP extension received"); + +#if defined(WOLFSSL_TLS13) + if (IsAtLeastTLSv1_3(ssl->version)) { + if (msgType != client_hello && + msgType != encrypted_extensions) + return EXT_NOT_ALLOWED; + } + else +#endif + { + if (msgType != client_hello && + msgType != server_hello) + return EXT_NOT_ALLOWED; + } ret = SRTP_PARSE(ssl, input + offset, size, isRequest); break; #endif @@ -17864,6 +17897,15 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType, #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) case TLSX_ECH: WOLFSSL_MSG("ECH extension received"); + if (!IsAtLeastTLSv1_3(ssl->version)) + break; + + if (msgType != client_hello && + msgType != encrypted_extensions && + msgType != hello_retry_request) { + return EXT_NOT_ALLOWED; + } + ret = ECH_PARSE(ssl, input + offset, size, msgType); break; #endif diff --git a/tests/api.c b/tests/api.c index 2b259d54ab..60700334ca 100644 --- a/tests/api.c +++ b/tests/api.c @@ -37368,6 +37368,8 @@ TEST_CASE testCases[] = { TEST_DECL(test_certificate_authorities_client_hello), TEST_DECL(test_TLSX_TCA_Find), TEST_DECL(test_TLSX_SNI_GetSize_overflow), + TEST_DECL(test_TLSX_ECH_msg_type_validation), + TEST_DECL(test_TLSX_SRTP_msg_type_validation), TEST_DECL(test_wolfSSL_wolfSSL_UseSecureRenegotiation), TEST_DECL(test_wolfSSL_clear_secure_renegotiation), TEST_DECL(test_wolfSSL_SCR_Reconnect), diff --git a/tests/api/test_ossl_cipher.c b/tests/api/test_ossl_cipher.c index 096a352ce5..f40b097546 100644 --- a/tests/api/test_ossl_cipher.c +++ b/tests/api/test_ossl_cipher.c @@ -203,6 +203,34 @@ int test_wolfSSL_DES_ncbc(void) return EXPECT_RESULT(); } +int test_wolfSSL_DES_ncbc_zero_length(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_DES3) + const_DES_cblock myDes; + DES_cblock iv; + DES_cblock ivSaved; + DES_key_schedule key = {0}; + unsigned char msg[DES_BLOCK_SIZE] = {0}; + unsigned char out[DES_BLOCK_SIZE] = {0}; + + DES_set_key(&key, &myDes); + + /* length == 0 must no-op: the offset math would otherwise underflow + * size_t and read from a wild pointer. */ + XMEMSET((byte*)&iv, 0xAB, DES_BLOCK_SIZE); + XMEMCPY(&ivSaved, &iv, DES_BLOCK_SIZE); + DES_ncbc_encrypt(msg, out, 0, &myDes, &iv, DES_ENCRYPT); + ExpectIntEQ(XMEMCMP(&iv, &ivSaved, DES_BLOCK_SIZE), 0); + + XMEMSET((byte*)&iv, 0xAB, DES_BLOCK_SIZE); + XMEMCPY(&ivSaved, &iv, DES_BLOCK_SIZE); + DES_ncbc_encrypt(msg, out, 0, &myDes, &iv, DES_DECRYPT); + ExpectIntEQ(XMEMCMP(&iv, &ivSaved, DES_BLOCK_SIZE), 0); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_DES_ecb_encrypt(void) { EXPECT_DECLS; diff --git a/tests/api/test_ossl_cipher.h b/tests/api/test_ossl_cipher.h index caa49928b4..a57657d0c5 100644 --- a/tests/api/test_ossl_cipher.h +++ b/tests/api/test_ossl_cipher.h @@ -26,6 +26,7 @@ int test_wolfSSL_DES(void); int test_wolfSSL_DES_ncbc(void); +int test_wolfSSL_DES_ncbc_zero_length(void); int test_wolfSSL_DES_ecb_encrypt(void); int test_wolfSSL_DES_ede3_cbc_encrypt(void); int test_wolfSSL_AES_encrypt(void); @@ -38,6 +39,7 @@ int test_wolfSSL_RC4(void); #define TEST_OSSL_CIPHER_DECLS \ TEST_DECL_GROUP("ossl_cipher", test_wolfSSL_DES), \ TEST_DECL_GROUP("ossl_cipher", test_wolfSSL_DES_ncbc), \ + TEST_DECL_GROUP("ossl_cipher", test_wolfSSL_DES_ncbc_zero_length), \ TEST_DECL_GROUP("ossl_cipher", test_wolfSSL_DES_ecb_encrypt), \ TEST_DECL_GROUP("ossl_cipher", test_wolfSSL_DES_ede3_cbc_encrypt), \ TEST_DECL_GROUP("ossl_cipher", test_wolfSSL_AES_encrypt), \ diff --git a/tests/api/test_tls13.c b/tests/api/test_tls13.c index 194ea6e432..5833edf4a7 100644 --- a/tests/api/test_tls13.c +++ b/tests/api/test_tls13.c @@ -5254,3 +5254,29 @@ int test_tls13_serverhello_bad_cipher_suites(void) #endif return EXPECT_RESULT(); } + +int test_tls13_clear_preserves_psk_dhe(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)) && \ + defined(WOLFSSL_TLS13) && defined(HAVE_SUPPORTED_CURVES) && \ + (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)) && \ + !defined(NO_WOLFSSL_CLIENT) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method())); + ExpectIntEQ(wolfSSL_CTX_no_dhe_psk(ctx), 0); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectIntEQ(ssl->options.noPskDheKe, 1); + + /* SSL reuse must preserve the CTX-level noPskDheKe; resetting to 0 + * would silently re-enable psk_dhe_ke for the next handshake. */ + ExpectIntEQ(wolfSSL_clear(ssl), WOLFSSL_SUCCESS); + ExpectIntEQ(ssl->options.noPskDheKe, 1); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_tls13.h b/tests/api/test_tls13.h index c8b42fc56c..7facadb9a8 100644 --- a/tests/api/test_tls13.h +++ b/tests/api/test_tls13.h @@ -60,6 +60,7 @@ int test_tls13_cert_with_extern_psk_requires_key_share(void); int test_tls13_cert_with_extern_psk_rejects_resumption(void); int test_tls13_cert_with_extern_psk_sh_missing_key_share(void); int test_tls13_cert_with_extern_psk_sh_confirms_resumption(void); +int test_tls13_clear_preserves_psk_dhe(void); #define TEST_TLS13_DECLS \ TEST_DECL_GROUP("tls13", test_tls13_apis), \ @@ -97,6 +98,7 @@ int test_tls13_cert_with_extern_psk_sh_confirms_resumption(void); TEST_DECL_GROUP("tls13", test_tls13_cert_with_extern_psk_requires_key_share), \ TEST_DECL_GROUP("tls13", test_tls13_cert_with_extern_psk_rejects_resumption), \ TEST_DECL_GROUP("tls13", test_tls13_cert_with_extern_psk_sh_missing_key_share), \ - TEST_DECL_GROUP("tls13", test_tls13_cert_with_extern_psk_sh_confirms_resumption) + TEST_DECL_GROUP("tls13", test_tls13_cert_with_extern_psk_sh_confirms_resumption), \ + TEST_DECL_GROUP("tls13", test_tls13_clear_preserves_psk_dhe) #endif /* WOLFCRYPT_TEST_TLS13_H */ diff --git a/tests/api/test_tls_ext.c b/tests/api/test_tls_ext.c index ad6053f727..755a31938f 100644 --- a/tests/api/test_tls_ext.c +++ b/tests/api/test_tls_ext.c @@ -609,3 +609,54 @@ int test_TLSX_SNI_GetSize_overflow(void) #endif return EXPECT_RESULT(); } + +/* ECH is only valid in ClientHello, EncryptedExtensions, or + * HelloRetryRequest per RFC 9460. Feeding it in a Certificate message must + * be rejected with EXT_NOT_ALLOWED rather than being silently accepted. */ +int test_TLSX_ECH_msg_type_validation(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + /* type = TLSX_ECH (0xfe0d), size = 0x0000 */ + const byte extBytes[] = { 0xfe, 0x0d, 0x00, 0x00 }; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + ExpectIntEQ(TLSX_Parse(ssl, extBytes, (word16)sizeof(extBytes), + certificate, NULL), + WC_NO_ERR_TRACE(EXT_NOT_ALLOWED)); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* use_srtp is only valid in ClientHello/ServerHello (pre-TLS 1.3) or + * ClientHello/EncryptedExtensions (TLS 1.3) per RFC 5764. Feeding it in a + * Certificate message must be rejected with EXT_NOT_ALLOWED. */ +int test_TLSX_SRTP_msg_type_validation(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_SRTP) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + /* type = TLSX_USE_SRTP (0x000e), size = 0x0000 */ + const byte extBytes[] = { 0x00, 0x0e, 0x00, 0x00 }; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + ExpectIntEQ(TLSX_Parse(ssl, extBytes, (word16)sizeof(extBytes), + certificate, NULL), + WC_NO_ERR_TRACE(EXT_NOT_ALLOWED)); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_tls_ext.h b/tests/api/test_tls_ext.h index ec7a5223be..901ed14403 100644 --- a/tests/api/test_tls_ext.h +++ b/tests/api/test_tls_ext.h @@ -28,5 +28,7 @@ int test_certificate_authorities_certificate_request(void); int test_certificate_authorities_client_hello(void); int test_TLSX_TCA_Find(void); int test_TLSX_SNI_GetSize_overflow(void); +int test_TLSX_ECH_msg_type_validation(void); +int test_TLSX_SRTP_msg_type_validation(void); #endif /* TESTS_API_TEST_TLS_EMS_H */ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 9e89e64b0b..437abbcf39 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3208,7 +3208,10 @@ WOLFSSL_LOCAL int TLSX_ParseVersion(WOLFSSL* ssl, const byte* input, WOLFSSL_LOCAL int TLSX_SupportedVersions_Parse(const WOLFSSL* ssl, const byte* input, word16 length, byte msgType, ProtocolVersion* pv, Options* opts, TLSX** exts); -WOLFSSL_LOCAL int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, +#ifdef WOLFSSL_API_PREFIX_MAP + #define TLSX_Parse wolfSSL_TLSX_Parse +#endif +WOLFSSL_TEST_VIS int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType, Suites *suites); WOLFSSL_LOCAL int TLSX_Push(TLSX** list, TLSX_Type type, const void* data, void* heap);