diff --git a/src/ssl.c b/src/ssl.c index 763757e21fc..1ba84a7dfbd 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -5931,6 +5931,10 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out, #ifdef HAVE_SESSION_TICKET #ifdef WOLFSSL_TLS13 ssl->options.ticketsSent = 0; + #ifndef NO_WOLFSSL_SERVER + if (ssl->options.side == WOLFSSL_SERVER_END) + ssl->session->ticketNonce.len = 0; + #endif #if !defined(NO_WOLFSSL_SERVER) && \ defined(WOLFSSL_TLS13_TICKET_CHECK_PSK_MODES) /* Recorded from the ClientHello, so it must not carry into the next diff --git a/src/tls13.c b/src/tls13.c index d000adb23ed..30dac8331dd 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -7116,6 +7116,10 @@ static int DoPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 inputSz, ssl->session->ticketNonce.len, ssl->arrays->psk_key)) != 0) { goto cleanup; } + /* The resumed ticket's nonce has derived its PSK. The tickets of + * this connection get PSKs from a new resumption master secret, + * so their nonces count from 0 again (RFC 8446 Section 4.6.1). */ + ssl->session->ticketNonce.len = 0; /* Derive the early secret using the PSK. */ ret = DeriveEarlySecret(ssl); diff --git a/tests/api/test_tls13.c b/tests/api/test_tls13.c index 17ded2802ee..b43cc6e6643 100644 --- a/tests/api/test_tls13.c +++ b/tests/api/test_tls13.c @@ -14127,6 +14127,155 @@ int test_tls13_new_session_ticket_keeps_ems(void) return EXPECT_RESULT(); } +#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) +/* One connection, resumed from *sess when set. The server issues one ticket, + * or two with sendTicket. The newest ticket, with nonce expected, replaces + * *sess. */ +static int test_tls13_ticket_nonce_round(WOLFSSL_CTX* ctx_c, + WOLFSSL_CTX* ctx_s, struct test_memio_ctx* test_ctx, + WOLFSSL_SESSION** sess, int sendTicket, byte expected) +{ + EXPECT_DECLS; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + char buf[64]; + + test_memio_clear_buffer(test_ctx, 0); + test_memio_clear_buffer(test_ctx, 1); + ExpectIntEQ(test_memio_setup(test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + if (*sess != NULL) + ExpectIntEQ(wolfSSL_set_session(ssl_c, *sess), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_c), *sess != NULL); + if (sendTicket) + ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s), WOLFSSL_SUCCESS); + + /* Consume the NewSessionTicket messages. */ + ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), + WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR), + WOLFSSL_ERROR_WANT_READ); + + /* Both ends hold the newest ticket. */ + if (ssl_c != NULL) { + ExpectIntEQ(ssl_c->session->ticketNonce.len, DEF_TICKET_NONCE_SZ); + ExpectIntEQ(ssl_c->session->ticketNonce.data[0], expected); + } + if (ssl_s != NULL) { + ExpectIntEQ(ssl_s->session->ticketNonce.len, DEF_TICKET_NONCE_SZ); + ExpectIntEQ(ssl_s->session->ticketNonce.data[0], expected); + } + + wolfSSL_SESSION_free(*sess); + *sess = NULL; + ExpectNotNull(*sess = wolfSSL_get1_session(ssl_c)); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + return EXPECT_RESULT(); +} +#endif + +/* The nonce only has to be unique among the tickets of one connection + * (RFC 8446 Section 4.6.1), so each connection numbers its tickets from 0. + * The nonce restored from a resumed ticket derives the PSK and is no starting + * point for the count: a chain of resumptions, each from the newest ticket, + * must not run the one byte nonce into SESSION_TICKET_NONCE_OVERFLOW. */ +int test_tls13_ticket_nonce_per_connection(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL_SESSION* sess = NULL; + struct test_memio_ctx test_ctx; + int i; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + + /* A full handshake, then more resumptions than a one byte nonce can + * count, each from the ticket of the connection before. */ + for (i = 0; i <= 256 && EXPECT_SUCCESS(); i++) { + ExpectIntEQ(test_tls13_ticket_nonce_round(ctx_c, ctx_s, &test_ctx, + &sess, 0, 0), TEST_SUCCESS); + } + /* A second ticket on a resumed connection is numbered after the first + * one, and the connection resumed from it counts from 0 again. */ + ExpectIntEQ(test_tls13_ticket_nonce_round(ctx_c, ctx_s, &test_ctx, &sess, + 1, 1), TEST_SUCCESS); + ExpectIntEQ(test_tls13_ticket_nonce_round(ctx_c, ctx_s, &test_ctx, &sess, + 0, 0), TEST_SUCCESS); + + wolfSSL_SESSION_free(sess); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* A server object reused with wolfSSL_clear() keeps its session and with it + * the nonce of the last ticket. The connection after it numbers from 0. */ +int test_tls13_ticket_nonce_server_reuse(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \ + !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; + char buf[64]; + int i; + + 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); + + for (i = 0; i < 3 && EXPECT_SUCCESS(); i++) { + if (i > 0) { + /* The same server object, a fresh client. */ + ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS); + test_memio_clear_buffer(&test_ctx, 0); + test_memio_clear_buffer(&test_ctx, 1); + wolfSSL_free(ssl_c); + ssl_c = NULL; + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, + NULL, wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), + 0); + } + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Consume the NewSessionTicket. */ + ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), + WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR), + WOLFSSL_ERROR_WANT_READ); + if (ssl_c != NULL) { + ExpectIntEQ(ssl_c->session->ticketNonce.len, DEF_TICKET_NONCE_SZ); + ExpectIntEQ(ssl_c->session->ticketNonce.data[0], 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. */ diff --git a/tests/api/test_tls13.h b/tests/api/test_tls13.h index 45b1035138d..394cd7366b8 100644 --- a/tests/api/test_tls13.h +++ b/tests/api/test_tls13.h @@ -162,6 +162,8 @@ int test_tls13_ticket_psk_modes_uses_policy(void); int test_tls13_send_session_ticket_psk_modes(void); int test_tls13_new_session_ticket_ext_framing(void); int test_tls13_new_session_ticket_keeps_ems(void); +int test_tls13_ticket_nonce_per_connection(void); +int test_tls13_ticket_nonce_server_reuse(void); int test_tls13_ignore_legacy_record_version(void); int test_tls13_hs_secret_zeroized_psk_ke(void); int test_tls13_hs_secret_zeroized_sha384(void); @@ -309,6 +311,8 @@ int test_tls13_export_client_key_update(void); TEST_DECL_GROUP("tls13", test_tls13_send_session_ticket_psk_modes), \ TEST_DECL_GROUP("tls13", test_tls13_new_session_ticket_ext_framing), \ TEST_DECL_GROUP("tls13", test_tls13_new_session_ticket_keeps_ems), \ + TEST_DECL_GROUP("tls13", test_tls13_ticket_nonce_per_connection), \ + TEST_DECL_GROUP("tls13", test_tls13_ticket_nonce_server_reuse), \ TEST_DECL_GROUP("tls13", test_tls13_psk_mode_mismatch_falls_back), \ TEST_DECL_GROUP("tls13", test_tls13_ticket_psk_modes_uses_policy), \ TEST_DECL_GROUP("tls13", test_tls13_pha_status_request), \