Skip to content

Commit

Permalink
feat: Add API to gate session tickets to TLS1.3 only (#4645)
Browse files Browse the repository at this point in the history
  • Loading branch information
maddeleine authored Jul 11, 2024
1 parent 2c4d012 commit a99299a
Show file tree
Hide file tree
Showing 6 changed files with 259 additions and 3 deletions.
16 changes: 16 additions & 0 deletions api/s2n.h
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,22 @@ S2N_API extern int s2n_config_set_ticket_decrypt_key_lifetime(struct s2n_config
*/
S2N_API extern int s2n_config_add_ticket_crypto_key(struct s2n_config *config, const uint8_t *name, uint32_t name_len,
uint8_t *key, uint32_t key_len, uint64_t intro_time_in_seconds_from_epoch);

/**
* Requires that session tickets are only used when forward secrecy is possible.
*
* Restricts session resumption to TLS1.3, as the tickets used in TLS1.2 resumption are
* not forward secret. Clients should not expect to receive new session tickets and servers
* will not send new session tickets when TLS1.2 is negotiated and ticket forward secrecy is required.
*
* @note The default behavior is that forward secrecy is not required.
*
* @param config The config object being updated
* @param enabled Indicates if forward secrecy is required or not on tickets
* @returns S2N_SUCCESS on success. S2N_FAILURE on failure
*/
S2N_API extern int s2n_config_require_ticket_forward_secrecy(struct s2n_config *config, bool enabled);

/**
* Sets user defined context on the `s2n_config` object.
*
Expand Down
226 changes: 226 additions & 0 deletions tests/unit/s2n_session_ticket_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -1478,6 +1478,232 @@ int main(int argc, char **argv)
EXPECT_EQUAL(client->ticket_lifetime_hint, 0);
}

/* Test: Server disables tls12 tickets */
{
DEFER_CLEANUP(struct s2n_config *forward_secret_config = s2n_config_new(),
s2n_config_ptr_free);
EXPECT_NOT_NULL(forward_secret_config);
EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true));
EXPECT_SUCCESS(s2n_config_set_cipher_preferences(forward_secret_config, "default_tls13"));
EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(forward_secret_config,
chain_and_key));
EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(forward_secret_config, 1));
EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(forward_secret_config, ticket_key_name1,
s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0));

DEFER_CLEANUP(struct s2n_config *tls12_client_config = s2n_config_new(),
s2n_config_ptr_free);
EXPECT_NOT_NULL(tls12_client_config);
EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls12_client_config, 1));
EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(tls12_client_config));
/* Security policy that does not support TLS1.3 */
EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_client_config, "20240501"));

DEFER_CLEANUP(struct s2n_config *tls13_client_config = s2n_config_new(),
s2n_config_ptr_free);
EXPECT_NOT_NULL(tls12_client_config);
EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls13_client_config, 1));
EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(tls13_client_config));
/* Security policy that does support TLS1.3 */
EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_client_config, "default_tls13"));

/* Server does not send ticket when forward secrecy is enforced and TLS1.2 is negotiated */
{
DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(client);
DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(server);

EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config));
EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config));

DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 },
s2n_io_stuffer_pair_free);
EXPECT_OK(s2n_io_stuffer_pair_init(&test_io));
EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io));

EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client));
EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12);
EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12);

uint16_t tickets_sent = 0;
EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent));
EXPECT_EQUAL(tickets_sent, 0);
}

/* Server does send tickets when forward secrecy is enforced and TLS1.3 is negotiated */
if (s2n_is_tls13_fully_supported()) {
DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(client);
DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(server);

EXPECT_SUCCESS(s2n_connection_set_config(client, tls13_client_config));
EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config));

DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 },
s2n_io_stuffer_pair_free);
EXPECT_OK(s2n_io_stuffer_pair_init(&test_io));
EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io));

EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client));
EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13);
EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13);

uint16_t tickets_sent = 0;
EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent));
EXPECT_EQUAL(tickets_sent, 1);
}

/* Server does not accept valid TLS1.2 ticket when forward secrecy is enforced */
{
DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(client);
DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(server);

/* Disable forward secrecy for the first handshake */
EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, false));

EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config));
EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config));

DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 },
s2n_io_stuffer_pair_free);
EXPECT_OK(s2n_io_stuffer_pair_init(&test_io));
EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io));

EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client));
EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12);
EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12);

uint16_t tickets_sent = 0;
EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent));
EXPECT_EQUAL(tickets_sent, 1);

uint8_t session_data[S2N_TLS12_SESSION_SIZE] = { 0 };
EXPECT_SUCCESS(s2n_connection_get_session(client, session_data, sizeof(session_data)));

/* Enable forward secrecy for the second handshake */
EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true));

EXPECT_SUCCESS(s2n_connection_wipe(client));
EXPECT_SUCCESS(s2n_connection_wipe(server));

EXPECT_SUCCESS(s2n_connection_set_session(client, session_data, sizeof(session_data)));

EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config));
EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config));
EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io));

EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client));
EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12);
EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12);

/* Session ticket not accepted */
EXPECT_TRUE(IS_FULL_HANDSHAKE(client));
EXPECT_TRUE(IS_FULL_HANDSHAKE(server));

/* No ticket issued */
EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent));
EXPECT_EQUAL(tickets_sent, 0);
}
}

/* Test: Client disables tls12 tickets */
{
DEFER_CLEANUP(struct s2n_config *forward_secret_config = s2n_config_new(),
s2n_config_ptr_free);
EXPECT_NOT_NULL(forward_secret_config);
EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true));
EXPECT_SUCCESS(s2n_config_set_cipher_preferences(forward_secret_config, "default_tls13"));
EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(forward_secret_config, 1));
EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(forward_secret_config));

DEFER_CLEANUP(struct s2n_config *tls12_server_config = s2n_config_new(),
s2n_config_ptr_free);
EXPECT_NOT_NULL(tls12_server_config);
EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls12_server_config, 1));
/* Security policy that does not support TLS1.3 */
EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_server_config, "20240501"));
EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls12_server_config,
chain_and_key));
EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(tls12_server_config, ticket_key_name1,
s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0));

DEFER_CLEANUP(struct s2n_config *tls13_server_config = s2n_config_new(),
s2n_config_ptr_free);
EXPECT_NOT_NULL(tls13_server_config);
EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls13_server_config, 1));
/* Security policy that does support TLS1.3 */
EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_server_config, "default_tls13"));
EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls13_server_config,
chain_and_key));
EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(tls13_server_config, ticket_key_name1,
s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0));

/* No ticket is received when forward secrecy is enforced and TLS1.2 is negotiated */
{
DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(client);
DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(server);

EXPECT_SUCCESS(s2n_connection_set_config(client, forward_secret_config));
EXPECT_SUCCESS(s2n_connection_set_config(server, tls12_server_config));

DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 },
s2n_io_stuffer_pair_free);
EXPECT_OK(s2n_io_stuffer_pair_init(&test_io));
EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io));

EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client));

EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12);
EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12);

EXPECT_EQUAL(client->client_ticket.size, 0);
}

/* A ticket is received when forward secrecy is enforced and TLS1.3 is negotiated */
if (s2n_is_tls13_fully_supported()) {
DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(client);
DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(server);

EXPECT_SUCCESS(s2n_connection_set_config(client, forward_secret_config));
EXPECT_SUCCESS(s2n_connection_set_config(server, tls13_server_config));

DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 },
s2n_io_stuffer_pair_free);
EXPECT_OK(s2n_io_stuffer_pair_init(&test_io));
EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io));

EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client));

EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13);
EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13);

/* Do a recv call to pick up TLS1.3 ticket */
uint8_t data = 1;
s2n_blocked_status blocked = S2N_NOT_BLOCKED;
EXPECT_FAILURE_WITH_ERRNO(s2n_recv(client, &data, 1, &blocked), S2N_ERR_IO_BLOCKED);

EXPECT_NOT_EQUAL(client->client_ticket.size, 0);
}
}

EXPECT_SUCCESS(s2n_io_pair_close(&io_pair));
EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key));
EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_chain_and_key));
Expand Down
6 changes: 4 additions & 2 deletions tls/extensions/s2n_client_session_ticket.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ const s2n_extension_type s2n_client_session_ticket_extension = {

static bool s2n_client_session_ticket_should_send(struct s2n_connection *conn)
{
return conn->config->use_tickets && !s2n_client_psk_should_send(conn);
return conn->config->use_tickets && !conn->config->ticket_forward_secrecy
&& !s2n_client_psk_should_send(conn);
}

static int s2n_client_session_ticket_send(struct s2n_connection *conn, struct s2n_stuffer *out)
Expand All @@ -50,7 +51,8 @@ static int s2n_client_session_ticket_send(struct s2n_connection *conn, struct s2

static int s2n_client_session_ticket_recv(struct s2n_connection *conn, struct s2n_stuffer *extension)
{
if (conn->config->use_tickets != 1 || conn->actual_protocol_version > S2N_TLS12) {
if (conn->config->use_tickets != 1 || conn->actual_protocol_version > S2N_TLS12
|| conn->config->ticket_forward_secrecy) {
/* Ignore the extension. */
return S2N_SUCCESS;
}
Expand Down
3 changes: 2 additions & 1 deletion tls/extensions/s2n_server_session_ticket.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ const s2n_extension_type s2n_server_session_ticket_extension = {

static bool s2n_session_ticket_should_send(struct s2n_connection *conn)
{
return s2n_server_sending_nst(conn) && s2n_connection_get_protocol_version(conn) < S2N_TLS13;
return s2n_server_sending_nst(conn) && s2n_connection_get_protocol_version(conn) < S2N_TLS13
&& !conn->config->ticket_forward_secrecy;
}

static int s2n_session_ticket_recv(struct s2n_connection *conn, struct s2n_stuffer *extension)
Expand Down
9 changes: 9 additions & 0 deletions tls/s2n_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,15 @@ int s2n_config_add_ticket_crypto_key(struct s2n_config *config,
return 0;
}

int s2n_config_require_ticket_forward_secrecy(struct s2n_config *config, bool enabled)
{
POSIX_ENSURE_REF(config);

config->ticket_forward_secrecy = enabled;

return S2N_SUCCESS;
}

int s2n_config_set_cert_tiebreak_callback(struct s2n_config *config, s2n_cert_tiebreak_callback cert_tiebreak_cb)
{
config->cert_tiebreak_cb = cert_tiebreak_cb;
Expand Down
2 changes: 2 additions & 0 deletions tls/s2n_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ struct s2n_config {

unsigned custom_blinding_set : 1;

unsigned ticket_forward_secrecy : 1;

struct s2n_dh_params *dhparams;
/* Needed until we can deprecate s2n_config_add_cert_chain_and_key. This is
* used to release memory allocated only in the deprecated API that the application
Expand Down

0 comments on commit a99299a

Please sign in to comment.