From c4061b12840cfbfa21b386f4e92aa57c7a52858e Mon Sep 17 00:00:00 2001 From: Reda Chouk Date: Wed, 9 Sep 2026 11:30:01 +0200 Subject: [PATCH] Zeroizization fixes in tls13 - wipe the whole preMasterSEcret buffer at the tls 1.3 handshake secret's last use as preMasterSz no longer describes its contents - zeroize the tls 1.3 early secret and teh psk it was extracted from at their last use in DeriveHandshakeSecret --- src/tls13.c | 13 ++- tests/api/test_tls13.c | 185 +++++++++++++++++++++++++++++++++++++++++ tests/api/test_tls13.h | 10 ++- 3 files changed, 204 insertions(+), 4 deletions(-) diff --git a/src/tls13.c b/src/tls13.c index b605c9e5e55..094936a75ce 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -1356,6 +1356,14 @@ int DeriveHandshakeSecret(WOLFSSL* ssl) mac2hash(ssl->specs.mac_algorithm)); PRIVATE_KEY_LOCK(); } + if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) { + /* Last use of the early secret and of the PSK it was extracted from - + * zeroize both. */ + ForceZero(ssl->arrays->secret, SECRET_LEN); +#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) + ForceZero(ssl->arrays->psk_key, MAX_PSK_KEY_LEN); +#endif + } #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Add("DeriveHandshakeSecret key", key, WC_MAX_DIGEST_SIZE); @@ -13758,7 +13766,7 @@ static int SendTls13Finished(WOLFSSL* ssl) ssl->kdfDeriveStep = TLS13_SEND_KDF_FIN_MASTER_SECRET; } /* Last use of preMasterSecret - zeroize as soon as possible. */ - ForceZero(ssl->arrays->preMasterSecret, ssl->arrays->preMasterSz); + ForceZero(ssl->arrays->preMasterSecret, ENCRYPT_LEN); #ifdef WOLFSSL_EARLY_DATA #ifdef WOLFSSL_DTLS13 @@ -15546,8 +15554,7 @@ int DoTls13MsgDerives(WOLFSSL* ssl, byte type) } /* Zeroized only after the derive completed: a pend retry * still reads preMasterSecret. */ - ForceZero(ssl->arrays->preMasterSecret, - ssl->arrays->preMasterSz); + ForceZero(ssl->arrays->preMasterSecret, ENCRYPT_LEN); ssl->kdfMsgStep = TLS13_MSG_KDF_FIN_MASTER_SECRET; } #ifdef WOLFSSL_EARLY_DATA diff --git a/tests/api/test_tls13.c b/tests/api/test_tls13.c index 18c640aae1b..0fc8f0bd176 100644 --- a/tests/api/test_tls13.c +++ b/tests/api/test_tls13.c @@ -11612,3 +11612,188 @@ int test_tls13_cryptocb_async(void) #endif return EXPECT_RESULT(); } + +/* A psk_ke handshake, where preMasterSz is 0, must leave no handshake secret + * behind in preMasterSecret. */ +int test_tls13_hs_secret_zeroized_psk_ke(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(HAVE_SUPPORTED_CURVES) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + + byte zeros[ENCRYPT_LEN]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(zeros, 0, sizeof(zeros)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_psk_client_callback(ssl_c, test_tls13_fnp_client_cb); + wolfSSL_set_psk_server_callback(ssl_s, test_tls13_fnp_server_cb); + + /* No key_share, so preMasterSz is zero when DeriveHandshakeSecret runs. */ + ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_c), 0); + ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_s), 0); + + wolfSSL_KeepArrays(ssl_c); + wolfSSL_KeepArrays(ssl_s); + XMEMSET(ssl_c->arrays->preMasterSecret, 0xAA, ENCRYPT_LEN); + XMEMSET(ssl_s->arrays->preMasterSecret, 0xAA, ENCRYPT_LEN); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(ssl_c->options.noPskDheKe, 1); + ExpectIntEQ(ssl_c->arrays->preMasterSz, 0); + + ExpectIntEQ(XMEMCMP(ssl_c->arrays->preMasterSecret, zeros, ENCRYPT_LEN), 0); + ExpectIntEQ(XMEMCMP(ssl_s->arrays->preMasterSecret, zeros, ENCRYPT_LEN), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* An X25519 handshake under TLS_AES_256_GCM_SHA384, where preMasterSz is 32 + * and the handshake secret 48 bytes, must leave none of it in preMasterSecret. + */ +int test_tls13_hs_secret_zeroized_sha384(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_CURVE25519) && \ + defined(BUILD_TLS_AES_256_GCM_SHA384) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte zeros[ENCRYPT_LEN]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(zeros, 0, sizeof(zeros)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + + ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "TLS13-AES256-GCM-SHA384"), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, "TLS13-AES256-GCM-SHA384"), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, WOLFSSL_ECC_X25519), + WOLFSSL_SUCCESS); + + wolfSSL_KeepArrays(ssl_c); + wolfSSL_KeepArrays(ssl_s); + XMEMSET(ssl_c->arrays->preMasterSecret, 0xAA, ENCRYPT_LEN); + XMEMSET(ssl_s->arrays->preMasterSecret, 0xAA, ENCRYPT_LEN); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(ssl_c->specs.hash_size, WC_SHA384_DIGEST_SIZE); + + ExpectIntEQ(XMEMCMP(ssl_c->arrays->preMasterSecret, zeros, ENCRYPT_LEN), 0); + ExpectIntEQ(XMEMCMP(ssl_s->arrays->preMasterSecret, zeros, ENCRYPT_LEN), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* A TLS 1.3 handshake must leave no early secret behind in arrays->secret. */ +int test_tls13_early_secret_zeroized(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte zeros[SECRET_LEN]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(zeros, 0, sizeof(zeros)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + + wolfSSL_KeepArrays(ssl_c); + wolfSSL_KeepArrays(ssl_s); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + ExpectIntEQ(XMEMCMP(ssl_c->arrays->secret, zeros, SECRET_LEN), 0); + ExpectIntEQ(XMEMCMP(ssl_s->arrays->secret, zeros, SECRET_LEN), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* An external PSK handshake must leave no PSK behind in arrays->psk_key. */ +int test_tls13_psk_key_zeroized(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(HAVE_SUPPORTED_CURVES) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte zeros[MAX_PSK_KEY_LEN]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(zeros, 0, sizeof(zeros)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_psk_client_callback(ssl_c, test_tls13_fnp_client_cb); + wolfSSL_set_psk_server_callback(ssl_s, test_tls13_fnp_server_cb); + + wolfSSL_KeepArrays(ssl_c); + wolfSSL_KeepArrays(ssl_s); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(ssl_s->options.isPSK, 1); + + ExpectIntEQ(XMEMCMP(ssl_c->arrays->psk_key, zeros, MAX_PSK_KEY_LEN), 0); + ExpectIntEQ(XMEMCMP(ssl_s->arrays->psk_key, zeros, MAX_PSK_KEY_LEN), 0); + ExpectIntEQ(XMEMCMP(ssl_c->arrays->secret, zeros, SECRET_LEN), 0); + ExpectIntEQ(XMEMCMP(ssl_s->arrays->secret, zeros, SECRET_LEN), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_tls13.h b/tests/api/test_tls13.h index 16323d61d3f..8afb5b50259 100644 --- a/tests/api/test_tls13.h +++ b/tests/api/test_tls13.h @@ -133,6 +133,10 @@ int test_tls13_pha_status_request(void); int test_tls13_x25519_keyshare_masks_reserved_bit(void); int test_tls13_is_init_finished_want_write(void); int test_tls13_cryptocb_async(void); +int test_tls13_hs_secret_zeroized_psk_ke(void); +int test_tls13_hs_secret_zeroized_sha384(void); +int test_tls13_early_secret_zeroized(void); +int test_tls13_psk_key_zeroized(void); #define TEST_TLS13_DECLS \ TEST_DECL_GROUP("tls13", test_tls13_apis), \ @@ -243,6 +247,10 @@ int test_tls13_cryptocb_async(void); TEST_DECL_GROUP("tls13", test_tls13_pha_status_request), \ TEST_DECL_GROUP("tls13", test_tls13_x25519_keyshare_masks_reserved_bit), \ TEST_DECL_GROUP("tls13", test_tls13_is_init_finished_want_write), \ - TEST_DECL_GROUP("tls13", test_tls13_cryptocb_async) + TEST_DECL_GROUP("tls13", test_tls13_cryptocb_async), \ + TEST_DECL_GROUP("tls13", test_tls13_hs_secret_zeroized_psk_ke), \ + TEST_DECL_GROUP("tls13", test_tls13_hs_secret_zeroized_sha384), \ + TEST_DECL_GROUP("tls13", test_tls13_early_secret_zeroized), \ + TEST_DECL_GROUP("tls13", test_tls13_psk_key_zeroized) #endif /* WOLFCRYPT_TEST_TLS13_H */