diff --git a/src/ssl_api_rw.c b/src/ssl_api_rw.c index 8d9349f4724..cbd8429dbf0 100644 --- a/src/ssl_api_rw.c +++ b/src/ssl_api_rw.c @@ -133,8 +133,6 @@ static int wolfssl_write_dup_take_tls13_work(WOLFSSL* ssl) * * @param [in, out] ssl SSL/TLS object of the write side. * @return 0 on success. - * @return BAD_MUTEX_E when the write duplicate could not be locked. Returned - * as-is by the caller, so ssl->error is not set for it. * @return WOLFSSL_FATAL_ERROR on error. Call wolfSSL_get_error() for the * reason. */ @@ -161,6 +159,7 @@ static int wolfssl_write_dup_do_tls13_work(WOLFSSL* ssl) if (ssl->postHandshakeAuthPending) { /* reset handshake states */ ssl->postHandshakeAuthPending = 0; + ssl->postHandshakeAuthPublish = 1; ssl->options.clientState = CLIENT_HELLO_COMPLETE; ssl->options.connectState = FIRST_REPLY_DONE; ssl->options.handShakeState = CLIENT_HELLO_COMPLETE; @@ -173,34 +172,6 @@ static int wolfssl_write_dup_do_tls13_work(WOLFSSL* ssl) } ret = WOLFSSL_FATAL_ERROR; } - /* PHA response fully sent: publish the write side's updated - * transcript to the read side for the next PHA round. */ - else if ((ssl->hsHashes != NULL) && (ssl->dupWrite != NULL)) { - if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) { - ret = BAD_MUTEX_E; - } - else { - int syncRet = InitHandshakeHashesAndCopy(ssl, - ssl->hsHashes, - &ssl->dupWrite->postHandshakeSyncedHashState); - if (syncRet != 0) { - /* On failure the copy may have left a partially - * initialized transcript. The read side only checks - * for non-NULL before consuming it, so drop it here to - * avoid hashing onto a corrupt transcript, and surface - * the error to the caller. */ - Free_HS_Hashes( - ssl->dupWrite->postHandshakeSyncedHashState, - ssl->heap); - ssl->dupWrite->postHandshakeSyncedHashState = NULL; - } - wc_UnLockMutex(&ssl->dupWrite->dupMutex); - if (syncRet != 0) { - ssl->error = syncRet; - ret = WOLFSSL_FATAL_ERROR; - } - } - } } #endif /* WOLFSSL_POST_HANDSHAKE_AUTH */ diff --git a/src/tls13.c b/src/tls13.c index 1a4e34e73c6..26b80da41bd 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -16701,6 +16701,32 @@ int DoTls13HandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx, #ifndef NO_WOLFSSL_CLIENT +#if defined(HAVE_WRITE_DUP) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) +static int Tls13WriteDupPublishPhaHashes(WOLFSSL* ssl) +{ + int ret = 0; + + if ((ssl->hsHashes != NULL) && (ssl->dupWrite != NULL)) { + if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) { + ret = BAD_MUTEX_E; + } + else { + ret = InitHandshakeHashesAndCopy(ssl, ssl->hsHashes, + &ssl->dupWrite->postHandshakeSyncedHashState); + if (ret != 0) { + /* Drop a partial copy so the read side never hashes onto it */ + Free_HS_Hashes(ssl->dupWrite->postHandshakeSyncedHashState, + ssl->heap); + ssl->dupWrite->postHandshakeSyncedHashState = NULL; + } + wc_UnLockMutex(&ssl->dupWrite->dupMutex); + } + } + + return ret; +} +#endif /* HAVE_WRITE_DUP && WOLFSSL_POST_HANDSHAKE_AUTH */ + /* The client connecting to the server. * The protocol version is expecting to be TLS v1.3. * If the server downgrades, and older versions of the protocol are compiled @@ -17140,6 +17166,17 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl) FreeAsyncCtx(ssl, 1); #endif + #if defined(HAVE_WRITE_DUP) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) + if (ssl->postHandshakeAuthPublish) { + ssl->postHandshakeAuthPublish = 0; + ssl->error = Tls13WriteDupPublishPhaHashes(ssl); + if (ssl->error != 0) { + WOLFSSL_ERROR(ssl->error); + return WOLFSSL_FATAL_ERROR; + } + } + #endif + ssl->error = 0; /* clear the error */ WOLFSSL_LEAVE("wolfSSL_connect_TLSv13", WOLFSSL_SUCCESS); diff --git a/tests/api.c b/tests/api.c index 1a1f9426ebe..d4ace150b98 100644 --- a/tests/api.c +++ b/tests/api.c @@ -42804,6 +42804,10 @@ static int test_write_dup(void) ExpectIntEQ(wolfSSL_get_chain_count(chain), 1); for (k = 0; k < 10; k++) EXCHANGE_DATA; + /* Second round hashes onto the first round's transcript. */ + ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS); + for (k = 0; k < 10 && !EXPECT_FAIL(); k++) + EXCHANGE_DATA; } #endif @@ -42942,6 +42946,10 @@ static int test_write_dup_want_write(void) ExpectIntEQ(wolfSSL_get_chain_count(chain), 1); for (k = 0; k < 10 && !EXPECT_FAIL(); k++) EXCHANGE_DATA; + /* Second round after a response that blocked once. */ + ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS); + for (k = 0; k < 10 && !EXPECT_FAIL(); k++) + EXCHANGE_DATA; } #endif /* WOLFSSL_POST_HANDSHAKE_AUTH && SESSION_CERTS */ @@ -43069,6 +43077,123 @@ static int test_write_dup_want_write_simul(void) return EXPECT_RESULT(); } +/* wolfSSL_negotiate() on the write side outside a PHA response leaves the + * transcript copy for the read side alone. */ +static int test_write_dup_negotiate_pha(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(HAVE_WRITE_DUP) && \ + defined(WOLFSSL_POST_HANDSHAKE_AUTH) && defined(WOLFSSL_TLS13) && \ + defined(SESSION_CERTS) + size_t i, k; + char hiWorld[] = "dup message"; + char readData[sizeof(hiWorld) + 5]; + struct { + method_provider client_meth; + method_provider server_meth; + const char* version_name; + } methods[] = { + {wolfTLSv1_3_client_method, wolfTLSv1_3_server_method, "TLS 1.3"}, +#ifdef WOLFSSL_DTLS13 + {wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method, "DTLS 1.3"}, +#endif + }; + +#define EXCHANGE_DATA do { \ + ExpectIntEQ(wolfSSL_write(ssl_s, hiWorld, sizeof(hiWorld)), \ + sizeof(hiWorld)); \ + ExpectIntEQ(wolfSSL_read(ssl_c, readData, sizeof(readData)), \ + sizeof(hiWorld)); \ + ExpectIntEQ(wolfSSL_write(ssl_c2, hiWorld, sizeof(hiWorld)), \ + sizeof(hiWorld)); \ + ExpectIntEQ(wolfSSL_read(ssl_s, readData, sizeof(readData)), \ + sizeof(hiWorld)); \ +} while (0) + + for (i = 0; i < XELEM_CNT(methods) && !EXPECT_FAIL(); i++) { + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + HS_Hashes* synced = NULL; + WOLFSSL *ssl_c2 = NULL; + WOLFSSL_X509_CHAIN* chain = NULL; + + if (i == 0) + printf("\n"); + printf("Testing write_dup negotiate and PHA %s... ", + methods[i].version_name); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + methods[i].client_meth, methods[i].server_meth), 0); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_allow_post_handshake_auth(ssl_c), 0); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, caCertFile, + NULL), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + ExpectNotNull(ssl_c2 = wolfSSL_write_dup(ssl_c)); + EXCHANGE_DATA; + + /* Before any PHA round there is nothing to copy. */ + ExpectIntEQ(wolfSSL_negotiate(ssl_c2), WOLFSSL_SUCCESS); + if (EXPECT_SUCCESS()) { + ExpectNull(ssl_c2->dupWrite->postHandshakeSyncedHashState); + } + + ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS); + for (k = 0; k < 3 && !EXPECT_FAIL(); k++) + EXCHANGE_DATA; + ExpectNotNull(chain = wolfSSL_get_peer_chain(ssl_s)); + ExpectIntEQ(wolfSSL_get_chain_count(chain), 1); + + /* After a round, negotiate does not copy the transcript again. Take + * the copy off so a new one shows up whatever address it gets. */ + if (EXPECT_SUCCESS()) { + synced = ssl_c2->dupWrite->postHandshakeSyncedHashState; + ssl_c2->dupWrite->postHandshakeSyncedHashState = NULL; + } + ExpectNotNull(synced); + ExpectIntEQ(wolfSSL_negotiate(ssl_c2), WOLFSSL_SUCCESS); + if (ssl_c2 != NULL) { + ExpectNull(ssl_c2->dupWrite->postHandshakeSyncedHashState); + if (ssl_c2->dupWrite->postHandshakeSyncedHashState == NULL) { + ssl_c2->dupWrite->postHandshakeSyncedHashState = synced; + } + else { + /* wolfSSL_free() releases this field too */ + ssl_c2->dupWrite->postHandshakeHashState = synced; + } + } + + ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS); + for (k = 0; k < 3 && !EXPECT_FAIL(); k++) + EXCHANGE_DATA; + + if (EXPECT_SUCCESS()) + printf("ok\n"); + else + printf("failed\n"); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_c2); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + } +#undef EXCHANGE_DATA +#endif + return EXPECT_RESULT(); +} + #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(HAVE_WRITE_DUP) && \ defined(HAVE_CHACHA) && defined(HAVE_POLY1305) && !defined(NO_SHA256) && \ (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \ @@ -47369,6 +47494,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_write_dup), TEST_DECL(test_write_dup_want_write), TEST_DECL(test_write_dup_want_write_simul), + TEST_DECL(test_write_dup_negotiate_pha), TEST_DECL(test_write_dup_oom), TEST_DECL(test_wc_SrpComputeKey_oom), TEST_DECL(test_read_write_hs), diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 3974ba87cbc..a811ce0d084 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -7605,6 +7605,7 @@ struct WOLFSSL { #ifdef WOLFSSL_TLS13 #ifdef WOLFSSL_POST_HANDSHAKE_AUTH WC_BITFIELD postHandshakeAuthPending:1; + WC_BITFIELD postHandshakeAuthPublish:1; #endif #endif #endif