mirror of https://github.com/wolfSSL/wolfssl.git
Only send TLS 1.3 NewSessionTicket when the client advertised PSK modes
RFC 9846 Section 4.3.9 says psk_key_exchange_modes restricts the PSKs the server may supply through NewSessionTicket, and Section 4.7.1 makes sending a ticket conditional on the ClientHello carrying a suitable extension. The send paths only checked that tickets were enabled. Record the modes from the ClientHello in Options - the extension object is freed with the rest of the handshake state, so wolfSSL_send_SessionTicket() cannot look it up. The automatic path skips the ticket; the explicit API returns MISSING_HANDSHAKE_DATA or PSK_KEY_ERROR. Define WOLFSSL_TLS13_TICKET_NO_PSK_MODES for the old behaviour. Fixes https://github.com/wolfSSL/wolfssl/issues/11126pull/11178/head
parent
252c3b1aa3
commit
456b7e6a27
10
src/tls.c
10
src/tls.c
|
|
@ -13060,8 +13060,16 @@ static int TLSX_PskKeModes_Parse(WOLFSSL* ssl, const byte* input, word16 length,
|
|||
byte modes;
|
||||
|
||||
ret = TLSX_PskKeyModes_Parse_Modes(input, length, msgType, &modes);
|
||||
if (ret == 0)
|
||||
if (ret == 0) {
|
||||
#if defined(HAVE_SESSION_TICKET) && !defined(NO_WOLFSSL_SERVER)
|
||||
/* Keep the advertised modes for the NewSessionTicket decision. The
|
||||
* extension object is dropped with the rest of the handshake state
|
||||
* once the handshake is done. */
|
||||
ssl->options.pskKeModes = modes;
|
||||
ssl->options.pskKeModesRecvd = 1;
|
||||
#endif
|
||||
ret = TLSX_PskKeyModes_Use(ssl, modes);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
WOLFSSL_ERROR_VERBOSE(ret);
|
||||
|
|
|
|||
63
src/tls13.c
63
src/tls13.c
|
|
@ -74,6 +74,11 @@
|
|||
* WOLFSSL_TICKET_HAVE_ID: Session tickets include ID default: off
|
||||
* Forced on when WOLFSSL_EARLY_DATA is set.
|
||||
* WOLFSSL_TICKET_NONCE_MALLOC: Dynamically allocate ticket nonce default: off
|
||||
* WOLFSSL_TLS13_TICKET_NO_PSK_MODES: Send NewSessionTicket even default: off
|
||||
* when the ClientHello advertised no usable
|
||||
* psk_key_exchange_modes. Restores the pre-check
|
||||
* behaviour; RFC 9846 Sections 4.3.9 and 4.7.1 say
|
||||
* the server should not send such a ticket.
|
||||
*
|
||||
* TLS 1.3 Key Exchange:
|
||||
* HAVE_KEYING_MATERIAL: Export keying material (RFC 8446 7.5) default: off
|
||||
|
|
@ -14596,6 +14601,48 @@ restore:
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Check the client advertised a PSK key exchange mode a resumption ticket can
|
||||
* be used with.
|
||||
*
|
||||
* RFC 9846 Section 4.3.9: psk_key_exchange_modes restricts both the PSKs
|
||||
* offered in the ClientHello and those the server might supply through
|
||||
* NewSessionTicket, and servers should not send tickets that are incompatible
|
||||
* with the advertised modes. RFC 9846 Section 4.7.1 makes sending a ticket
|
||||
* conditional on the client's hello carrying a suitable extension.
|
||||
*
|
||||
* ssl The SSL/TLS object.
|
||||
* returns 0 when a ticket may be sent, MISSING_HANDSHAKE_DATA when the
|
||||
* extension was not received and PSK_KEY_ERROR when none of the
|
||||
* advertised modes is usable.
|
||||
*/
|
||||
static int CheckTls13TicketPskModes(WOLFSSL* ssl)
|
||||
{
|
||||
#ifndef WOLFSSL_TLS13_TICKET_NO_PSK_MODES
|
||||
if (!ssl->options.pskKeModesRecvd) {
|
||||
WOLFSSL_MSG("No psk_key_exchange_modes in ClientHello");
|
||||
return MISSING_HANDSHAKE_DATA;
|
||||
}
|
||||
|
||||
if ((ssl->options.pskKeModes & (1 << PSK_KE)) != 0
|
||||
#ifdef HAVE_SUPPORTED_CURVES
|
||||
&& !ssl->options.onlyPskDheKe
|
||||
#endif
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
if ((ssl->options.pskKeModes & (1 << PSK_DHE_KE)) != 0 &&
|
||||
!ssl->options.noPskDheKe) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
WOLFSSL_MSG("No usable psk_key_exchange_modes advertised by client");
|
||||
return PSK_KEY_ERROR;
|
||||
#else
|
||||
(void)ssl;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Send New Session Ticket handshake message.
|
||||
* Message contains the information required to perform resumption.
|
||||
*
|
||||
|
|
@ -14620,6 +14667,12 @@ static int SendTls13NewSessionTicket(WOLFSSL* ssl)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (CheckTls13TicketPskModes(ssl) != 0) {
|
||||
WOLFSSL_MSG("Client advertised no usable PSK key exchange mode; "
|
||||
"skipping ticket");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WOLFSSL_DTLS13
|
||||
if (ssl->options.dtls)
|
||||
idx = Dtls13GetRlHeaderLength(ssl, 1) + DTLS_HANDSHAKE_HEADER_SZ;
|
||||
|
|
@ -18064,17 +18117,27 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
|
|||
* returns BAD_FUNC_ARG when ssl is NULL, or not using TLS v1.3,
|
||||
* SIDE_ERROR when not a server,
|
||||
* NOT_READY_ERROR when handshake not complete,
|
||||
* MISSING_HANDSHAKE_DATA when the ClientHello had no
|
||||
* psk_key_exchange_modes extension,
|
||||
* PSK_KEY_ERROR when no advertised PSK key exchange mode is usable,
|
||||
* WOLFSSL_FATAL_ERROR when creating or sending message fails, and
|
||||
* WOLFSSL_SUCCESS on success.
|
||||
*/
|
||||
int wolfSSL_send_SessionTicket(WOLFSSL* ssl)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version))
|
||||
return BAD_FUNC_ARG;
|
||||
if (ssl->options.side == WOLFSSL_CLIENT_END)
|
||||
return SIDE_ERROR;
|
||||
if (ssl->options.handShakeState != HANDSHAKE_DONE)
|
||||
return NOT_READY_ERROR;
|
||||
ret = CheckTls13TicketPskModes(ssl);
|
||||
if (ret != 0) {
|
||||
WOLFSSL_ERROR_VERBOSE(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ssl->error = SendTls13NewSessionTicket(ssl)) != 0) {
|
||||
WOLFSSL_ERROR(ssl->error);
|
||||
|
|
|
|||
|
|
@ -11612,3 +11612,153 @@ int test_tls13_cryptocb_async(void)
|
|||
#endif
|
||||
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(WOLFSSL_TLS13_TICKET_NO_PSK_MODES) && \
|
||||
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
||||
/* Drive a TLS 1.3 handshake up to, but not including, the server's final
|
||||
* wolfSSL_accept() - the call that runs the NewSessionTicket loop. */
|
||||
static int test_tls13_handshake_to_ticket(WOLFSSL* ssl_c, WOLFSSL* ssl_s)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
|
||||
/* ClientHello. */
|
||||
ExpectIntEQ(wolfSSL_connect(ssl_c), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
||||
WOLFSSL_ERROR_WANT_READ);
|
||||
/* Server flight. */
|
||||
ExpectIntEQ(wolfSSL_accept(ssl_s), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
||||
WOLFSSL_ERROR_WANT_READ);
|
||||
/* Client Finished. */
|
||||
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
||||
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* RFC 9846 Section 4.3.9 and Section 4.7.1: a NewSessionTicket creates a
|
||||
* resumption PSK, so the server may only send one when the ClientHello
|
||||
* advertised a psk_key_exchange_modes mode it can be used with. */
|
||||
int test_tls13_ticket_psk_modes(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(WOLFSSL_TLS13_TICKET_NO_PSK_MODES) && \
|
||||
!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;
|
||||
|
||||
/* A ClientHello without psk_key_exchange_modes gets no ticket. */
|
||||
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(test_tls13_handshake_to_ticket(ssl_c, ssl_s), TEST_SUCCESS);
|
||||
ExpectIntEQ(ssl_s->options.pskKeModesRecvd, 1);
|
||||
if (EXPECT_SUCCESS()) {
|
||||
ssl_s->options.pskKeModesRecvd = 0;
|
||||
}
|
||||
ExpectIntEQ(test_ctx.c_len, 0);
|
||||
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(test_ctx.c_len, 0);
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
ssl_c = NULL;
|
||||
wolfSSL_free(ssl_s);
|
||||
ssl_s = NULL;
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
ctx_c = NULL;
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
ctx_s = NULL;
|
||||
|
||||
/* Control: the wolfSSL client advertises the modes, so a ticket is sent. */
|
||||
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(test_tls13_handshake_to_ticket(ssl_c, ssl_s), TEST_SUCCESS);
|
||||
ExpectIntEQ(test_ctx.c_len, 0);
|
||||
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntGT(test_ctx.c_len, 0);
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* wolfSSL_send_SessionTicket() applies the same RFC 9846 precondition as the
|
||||
* automatic ticket path, and reports why it will not send. */
|
||||
int test_tls13_send_session_ticket_psk_modes(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(WOLFSSL_TLS13_TICKET_NO_PSK_MODES) && \
|
||||
!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;
|
||||
|
||||
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(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
|
||||
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s), WOLFSSL_SUCCESS);
|
||||
if (EXPECT_SUCCESS()) {
|
||||
/* No psk_key_exchange_modes extension in the ClientHello. */
|
||||
ssl_s->options.pskKeModesRecvd = 0;
|
||||
}
|
||||
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s),
|
||||
WC_NO_ERR_TRACE(MISSING_HANDSHAKE_DATA));
|
||||
if (EXPECT_SUCCESS()) {
|
||||
/* Extension present but carrying only unrecognized modes. */
|
||||
ssl_s->options.pskKeModesRecvd = 1;
|
||||
ssl_s->options.pskKeModes = 0;
|
||||
}
|
||||
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s),
|
||||
WC_NO_ERR_TRACE(PSK_KEY_ERROR));
|
||||
if (EXPECT_SUCCESS()) {
|
||||
/* psk_dhe_ke only, but the server refuses (EC)DHE with PSK. */
|
||||
ssl_s->options.pskKeModes = 1 << PSK_DHE_KE;
|
||||
ssl_s->options.noPskDheKe = 1;
|
||||
}
|
||||
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s),
|
||||
WC_NO_ERR_TRACE(PSK_KEY_ERROR));
|
||||
if (EXPECT_SUCCESS()) {
|
||||
ssl_s->options.noPskDheKe = 0;
|
||||
}
|
||||
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s), WOLFSSL_SUCCESS);
|
||||
#ifdef HAVE_SUPPORTED_CURVES
|
||||
if (EXPECT_SUCCESS()) {
|
||||
/* psk_ke only, but the server requires (EC)DHE with PSK. */
|
||||
ssl_s->options.pskKeModes = 1 << PSK_KE;
|
||||
ssl_s->options.onlyPskDheKe = 1;
|
||||
}
|
||||
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s),
|
||||
WC_NO_ERR_TRACE(PSK_KEY_ERROR));
|
||||
if (EXPECT_SUCCESS()) {
|
||||
ssl_s->options.onlyPskDheKe = 0;
|
||||
}
|
||||
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s), WOLFSSL_SUCCESS);
|
||||
#endif
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,6 +133,8 @@ 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_ticket_psk_modes(void);
|
||||
int test_tls13_send_session_ticket_psk_modes(void);
|
||||
|
||||
#define TEST_TLS13_DECLS \
|
||||
TEST_DECL_GROUP("tls13", test_tls13_apis), \
|
||||
|
|
@ -243,6 +245,8 @@ 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_ticket_psk_modes), \
|
||||
TEST_DECL_GROUP("tls13", test_tls13_send_session_ticket_psk_modes)
|
||||
|
||||
#endif /* WOLFCRYPT_TEST_TLS13_H */
|
||||
|
|
|
|||
|
|
@ -5452,6 +5452,9 @@ struct Options {
|
|||
#if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TLS13)
|
||||
unsigned int maxTicketTls13; /* maximum number of tickets to send */
|
||||
unsigned int ticketsSent; /* keep track of the total sent */
|
||||
#ifndef NO_WOLFSSL_SERVER
|
||||
byte pskKeModes; /* modes client advertised in CH */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* on/off or small bit flags, optimize layout */
|
||||
|
|
@ -5534,6 +5537,9 @@ struct Options {
|
|||
#ifdef WOLFSSL_EARLY_DATA
|
||||
word16 ticketPredatesCtx:1; /* PSK ticket minted before ctx */
|
||||
#endif
|
||||
#ifndef NO_WOLFSSL_SERVER
|
||||
word16 pskKeModesRecvd:1; /* CH had psk_key_exchange_modes */
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WOLFSSL_DTLS
|
||||
|
|
|
|||
Loading…
Reference in New Issue