Merge pull request #8913 from ronald-cron-arm/tls13-ticket-lifetime

TLS 1.3: Enforce ticket maximum lifetime and discard tickets with 0 lifetime
This commit is contained in:
Gilles Peskine 2024-03-14 15:59:25 +00:00 committed by GitHub
commit 7b333f1e88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 130 additions and 32 deletions

View File

@ -108,10 +108,16 @@ void mbedtls_ssl_ticket_init(mbedtls_ssl_ticket_context *ctx);
* least as strong as the strongest ciphersuite
* supported. Usually that means a 256-bit key.
*
* \note The lifetime of the keys is twice the lifetime of tickets.
* It is recommended to pick a reasonable lifetime so as not
* \note It is recommended to pick a reasonable lifetime so as not
* to negate the benefits of forward secrecy.
*
* \note The TLS 1.3 specification states that ticket lifetime must
* be smaller than seven days. If ticket lifetime has been
* set to a value greater than seven days in this module then
* if the TLS 1.3 is configured to send tickets after the
* handshake it will fail the connection when trying to send
* the first ticket.
*
* \return 0 if successful,
* or a specific MBEDTLS_ERR_XXX error code
*/
@ -145,10 +151,16 @@ int mbedtls_ssl_ticket_setup(mbedtls_ssl_ticket_context *ctx,
* \note \c klength must be sufficient for use by cipher specified
* to \c mbedtls_ssl_ticket_setup
*
* \note The lifetime of the keys is twice the lifetime of tickets.
* It is recommended to pick a reasonable lifetime so as not
* \note It is recommended to pick a reasonable lifetime so as not
* to negate the benefits of forward secrecy.
*
* \note The TLS 1.3 specification states that ticket lifetime must
* be smaller than seven days. If ticket lifetime has been
* set to a value greater than seven days in this module then
* if the TLS 1.3 is configured to send tickets after the
* handshake it will fail the connection when trying to send
* the first ticket.
*
* \return 0 if successful,
* or a specific MBEDTLS_ERR_XXX error code
*/

View File

@ -504,7 +504,7 @@ int mbedtls_ssl_ticket_parse(void *p_ticket,
#if defined(MBEDTLS_HAVE_TIME)
mbedtls_ms_time_t ticket_creation_time, ticket_age;
mbedtls_ms_time_t ticket_lifetime =
(mbedtls_ms_time_t) ctx->ticket_lifetime * 1000;
(mbedtls_ms_time_t) key->lifetime * 1000;
ret = mbedtls_ssl_session_get_ticket_creation_time(session,
&ticket_creation_time);

View File

@ -2917,12 +2917,17 @@ static int ssl_tls13_parse_new_session_ticket(mbedtls_ssl_context *ssl,
return ret;
}
/* session has been updated, allow export */
session->exported = 0;
return 0;
}
/* Non negative return values for ssl_tls13_postprocess_new_session_ticket().
* - POSTPROCESS_NEW_SESSION_TICKET_SIGNAL, all good, we have to signal the
* application that a valid ticket has been received.
* - POSTPROCESS_NEW_SESSION_TICKET_DISCARD, no fatal error, we keep the
* connection alive but we do not signal the ticket to the application.
*/
#define POSTPROCESS_NEW_SESSION_TICKET_SIGNAL 0
#define POSTPROCESS_NEW_SESSION_TICKET_DISCARD 1
MBEDTLS_CHECK_RETURN_CRITICAL
static int ssl_tls13_postprocess_new_session_ticket(mbedtls_ssl_context *ssl,
unsigned char *ticket_nonce,
@ -2934,6 +2939,10 @@ static int ssl_tls13_postprocess_new_session_ticket(mbedtls_ssl_context *ssl,
psa_algorithm_t psa_hash_alg;
int hash_length;
if (session->ticket_lifetime == 0) {
return POSTPROCESS_NEW_SESSION_TICKET_DISCARD;
}
#if defined(MBEDTLS_HAVE_TIME)
/* Store ticket creation time */
session->ticket_reception_time = mbedtls_ms_time();
@ -2990,7 +2999,7 @@ static int ssl_tls13_postprocess_new_session_ticket(mbedtls_ssl_context *ssl,
session, ssl->conf->tls13_kex_modes);
MBEDTLS_SSL_PRINT_TICKET_FLAGS(4, session->ticket_flags);
return 0;
return POSTPROCESS_NEW_SESSION_TICKET_SIGNAL;
}
/*
@ -3011,12 +3020,37 @@ static int ssl_tls13_process_new_session_ticket(mbedtls_ssl_context *ssl)
ssl, MBEDTLS_SSL_HS_NEW_SESSION_TICKET,
&buf, &buf_len));
/*
* We are about to update (maybe only partially) ticket data thus block
* any session export for the time being.
*/
ssl->session->exported = 1;
MBEDTLS_SSL_PROC_CHK(ssl_tls13_parse_new_session_ticket(
ssl, buf, buf + buf_len,
&ticket_nonce, &ticket_nonce_len));
MBEDTLS_SSL_PROC_CHK(ssl_tls13_postprocess_new_session_ticket(
ssl, ticket_nonce, ticket_nonce_len));
MBEDTLS_SSL_PROC_CHK_NEG(ssl_tls13_postprocess_new_session_ticket(
ssl, ticket_nonce, ticket_nonce_len));
switch (ret) {
case POSTPROCESS_NEW_SESSION_TICKET_SIGNAL:
/*
* All good, we have received a new valid ticket, session data can
* be exported now and we signal the ticket to the application.
*/
ssl->session->exported = 0;
ret = MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET;
break;
case POSTPROCESS_NEW_SESSION_TICKET_DISCARD:
ret = 0;
MBEDTLS_SSL_DEBUG_MSG(2, ("Discard new session ticket"));
break;
default:
ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR;
}
mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_HANDSHAKE_OVER);
@ -3133,10 +3167,6 @@ int mbedtls_ssl_tls13_handshake_client_step(mbedtls_ssl_context *ssl)
#if defined(MBEDTLS_SSL_SESSION_TICKETS)
case MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET:
ret = ssl_tls13_process_new_session_ticket(ssl);
if (ret != 0) {
break;
}
ret = MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET;
break;
#endif /* MBEDTLS_SSL_SESSION_TICKETS */

View File

@ -3266,20 +3266,21 @@ static int ssl_tls13_write_new_session_ticket_body(mbedtls_ssl_context *ssl,
MBEDTLS_SSL_DEBUG_RET(1, "write_ticket", ret);
return ret;
}
/* RFC 8446 4.6.1
/* RFC 8446 section 4.6.1
*
* ticket_lifetime: Indicates the lifetime in seconds as a 32-bit
* unsigned integer in network byte order from the time of ticket
* issuance. Servers MUST NOT use any value greater than
* 604800 seconds (7 days). The value of zero indicates that the
* ticket should be discarded immediately. Clients MUST NOT cache
* tickets for longer than 7 days, regardless of the ticket_lifetime,
* and MAY delete tickets earlier based on local policy. A server
* MAY treat a ticket as valid for a shorter period of time than what
* is stated in the ticket_lifetime.
* unsigned integer in network byte order from the time of ticket
* issuance. Servers MUST NOT use any value greater than
* 604800 seconds (7 days) ...
*/
if (ticket_lifetime > MBEDTLS_SSL_TLS1_3_MAX_ALLOWED_TICKET_LIFETIME) {
ticket_lifetime = MBEDTLS_SSL_TLS1_3_MAX_ALLOWED_TICKET_LIFETIME;
MBEDTLS_SSL_DEBUG_MSG(
1, ("Ticket lifetime (%u) is greater than 7 days.",
(unsigned int) ticket_lifetime));
return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
}
MBEDTLS_PUT_UINT32_BE(ticket_lifetime, p, 0);
MBEDTLS_SSL_DEBUG_MSG(3, ("ticket_lifetime: %u",
(unsigned int) ticket_lifetime));

View File

@ -3072,16 +3072,16 @@ reconnect:
frags++;
written += ret;
} while (written < len);
}
end_of_early_data:
buf[written] = '\0';
mbedtls_printf(
" %" MBEDTLS_PRINTF_SIZET " bytes of early data written in %" MBEDTLS_PRINTF_SIZET " fragments\n\n%s\n",
written,
frags,
(char *) buf);
buf[written] = '\0';
mbedtls_printf(
" %" MBEDTLS_PRINTF_SIZET " bytes of early data written in %" MBEDTLS_PRINTF_SIZET " fragments\n\n%s\n",
written,
frags,
(char *) buf);
}
#endif /* MBEDTLS_SSL_EARLY_DATA */
while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) {

View File

@ -13532,6 +13532,61 @@ run_test "TLS 1.3: NewSessionTicket: Basic check, m->m" \
-s "key exchange mode: psk_ephemeral" \
-s "found pre_shared_key extension"
requires_all_configs_enabled MBEDTLS_SSL_PROTO_TLS1_3 \
MBEDTLS_SSL_CLI_C MBEDTLS_SSL_SRV_C \
MBEDTLS_SSL_SESSION_TICKETS MBEDTLS_HAVE_TIME \
MBEDTLS_DEBUG_C \
MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
requires_any_configs_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED \
MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED
run_test "TLS 1.3 m->m: NewSessionTicket: Ticket lifetime max value (7d)" \
"$P_SRV debug_level=1 crt_file=data_files/server5.crt key_file=data_files/server5.key ticket_timeout=604800 tickets=1" \
"$P_CLI reco_mode=1 reconnect=1" \
0 \
-c "Protocol is TLSv1.3" \
-c "HTTP/1.0 200 OK" \
-c "got new session ticket" \
-c "Reconnecting with saved session... ok" \
-s "Protocol is TLSv1.3" \
-S "Ticket lifetime (604800) is greater than 7 days."
requires_all_configs_enabled MBEDTLS_SSL_PROTO_TLS1_3 \
MBEDTLS_SSL_CLI_C MBEDTLS_SSL_SRV_C \
MBEDTLS_SSL_SESSION_TICKETS MBEDTLS_HAVE_TIME \
MBEDTLS_DEBUG_C \
MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
requires_any_configs_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED \
MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED
run_test "TLS 1.3 m->m: NewSessionTicket: Ticket lifetime too long (7d + 1s)" \
"$P_SRV debug_level=1 crt_file=data_files/server5.crt key_file=data_files/server5.key ticket_timeout=604801 tickets=1" \
"$P_CLI reco_mode=1 reconnect=1" \
1 \
-c "Protocol is TLSv1.3" \
-C "HTTP/1.0 200 OK" \
-C "got new session ticket" \
-C "Reconnecting with saved session... ok" \
-S "Protocol is TLSv1.3" \
-s "Ticket lifetime (604801) is greater than 7 days."
requires_all_configs_enabled MBEDTLS_SSL_PROTO_TLS1_3 \
MBEDTLS_SSL_CLI_C MBEDTLS_SSL_SRV_C \
MBEDTLS_SSL_SESSION_TICKETS MBEDTLS_HAVE_TIME \
MBEDTLS_DEBUG_C \
MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
requires_any_configs_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED \
MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED
run_test "TLS 1.3 m->m: NewSessionTicket: ticket lifetime=0" \
"$P_SRV debug_level=2 crt_file=data_files/server5.crt key_file=data_files/server5.key ticket_timeout=0 tickets=1" \
"$P_CLI debug_level=2 reco_mode=1 reconnect=1" \
1 \
-c "Protocol is TLSv1.3" \
-c "HTTP/1.0 200 OK" \
-c "Discard new session ticket" \
-C "got new session ticket" \
-c "Reconnecting with saved session... failed" \
-s "Protocol is TLSv1.3" \
-s "<= write new session ticket"
requires_openssl_tls1_3_with_compatible_ephemeral
requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
requires_config_enabled MBEDTLS_DEBUG_C