Skip to content

Commit

Permalink
ktls: add method to track key updates (#4364)
Browse files Browse the repository at this point in the history
  • Loading branch information
lrstewart authored Jan 18, 2024
1 parent 028d315 commit 078d1e9
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 0 deletions.
18 changes: 18 additions & 0 deletions api/unstable/ktls.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ S2N_API int s2n_connection_ktls_enable_recv(struct s2n_connection *conn);
*
* Enabling TLS1.3 with this method is considered "unsafe" because the kernel
* currently doesn't support updating encryption keys, which is required in TLS1.3.
* s2n_connection_get_key_update_counts can be used to gather metrics on whether
* key updates are occurring on your connections before enabling TLS1.3.
*
* In order to safely enable TLS1.3, an application must ensure that its peer will
* not send any KeyUpdate messages. If s2n-tls receives a KeyUpdate message while
Expand All @@ -119,6 +121,22 @@ S2N_API int s2n_connection_ktls_enable_recv(struct s2n_connection *conn);
*/
S2N_API int s2n_config_ktls_enable_unsafe_tls13(struct s2n_config *config);

/**
* Reports the number of times sending and receiving keys have been updated.
*
* This only applies to TLS1.3. Earlier versions do not support key updates.
*
* @warning s2n-tls only tracks up to UINT8_MAX (255) key updates. If this method
* reports 255 updates, then more than 255 updates may have occurred.
*
* @param conn A pointer to the connection.
* @param send_key_updates Number of times the sending key was updated.
* @param recv_key_updates Number of times the receiving key was updated.
* @returns S2N_SUCCESS if successful, S2N_FAILURE otherwise.
*/
S2N_API int s2n_connection_get_key_update_counts(struct s2n_connection *conn,
uint8_t *send_key_updates, uint8_t *recv_key_updates);

/**
* Sends the contents of a file as application data.
*
Expand Down
28 changes: 28 additions & 0 deletions tests/unit/s2n_connection_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "tls/s2n_connection.h"

#include "api/unstable/ktls.h"
#include "crypto/s2n_hash.h"
#include "s2n_test.h"
#include "testlib/s2n_testlib.h"
Expand Down Expand Up @@ -878,6 +879,33 @@ int main(int argc, char **argv)
};
};

/* Test s2n_connection_get_key_update_counts */
{
/* Safety */
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);
uint8_t send_count = 0, recv_count = 0;
EXPECT_FAILURE_WITH_ERRNO(
s2n_connection_get_key_update_counts(NULL, &send_count, &recv_count),
S2N_ERR_NULL);
EXPECT_FAILURE_WITH_ERRNO(
s2n_connection_get_key_update_counts(conn, NULL, &recv_count),
S2N_ERR_NULL);
EXPECT_FAILURE_WITH_ERRNO(
s2n_connection_get_key_update_counts(conn, &send_count, NULL),
S2N_ERR_NULL);

/* Returns counts */
const uint8_t expected_send_count = 10;
conn->send_key_updated = expected_send_count;
const uint8_t expected_recv_count = 255;
conn->recv_key_updated = expected_recv_count;
EXPECT_SUCCESS(s2n_connection_get_key_update_counts(conn, &send_count, &recv_count));
EXPECT_EQUAL(send_count, expected_send_count);
EXPECT_EQUAL(recv_count, expected_recv_count);
}

EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_chain_and_key));
EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_chain_and_key));
END_TEST();
Expand Down
24 changes: 24 additions & 0 deletions tests/unit/s2n_key_update_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ int main(int argc, char **argv)
EXPECT_SUCCESS(s2n_key_update_recv(server_conn, &input));
EXPECT_EQUAL(server_conn->secure->client_sequence_number[0], 0);
EXPECT_FALSE(s2n_atomic_flag_test(&server_conn->key_update_pending));
EXPECT_EQUAL(server_conn->recv_key_updated, 1);

EXPECT_SUCCESS(s2n_connection_free(server_conn));
};
Expand All @@ -243,6 +244,7 @@ int main(int argc, char **argv)
EXPECT_SUCCESS(s2n_key_update_recv(client_conn, &input));
EXPECT_EQUAL(client_conn->secure->server_sequence_number[0], 0);
EXPECT_FALSE(s2n_atomic_flag_test(&client_conn->key_update_pending));
EXPECT_EQUAL(client_conn->recv_key_updated, 1);

EXPECT_SUCCESS(s2n_connection_free(client_conn));
};
Expand Down Expand Up @@ -294,6 +296,26 @@ int main(int argc, char **argv)
/* KeyUpdate still pending */
EXPECT_TRUE(s2n_atomic_flag_test(&conn->key_update_pending));
};

/* Receiving a KeyUpdate doesn't overflow the key update count */
{
DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free);
EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0));

DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);
conn->actual_protocol_version = S2N_TLS13;
conn->secure->cipher_suite = cipher_suite_with_limit;
conn->recv_key_updated = UINT8_MAX;

conn->secure->client_sequence_number[0] = 1;
EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, S2N_KEY_UPDATE_NOT_REQUESTED));
EXPECT_SUCCESS(s2n_key_update_recv(conn, &input));
EXPECT_EQUAL(conn->secure->client_sequence_number[0], 0);
EXPECT_EQUAL(conn->recv_key_updated, UINT8_MAX);
EXPECT_EQUAL(conn->send_key_updated, 0);
};
};

/* s2n_key_update_send */
Expand All @@ -319,6 +341,7 @@ int main(int argc, char **argv)
EXPECT_EQUAL(s2n_atomic_flag_test(&client_conn->key_update_pending), false);
EXPECT_BYTEARRAY_EQUAL(client_conn->secure->client_sequence_number, zeroed_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN);
EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) > 0);
EXPECT_EQUAL(client_conn->send_key_updated, 1);

EXPECT_SUCCESS(s2n_stuffer_free(&stuffer));
EXPECT_SUCCESS(s2n_connection_free(client_conn));
Expand Down Expand Up @@ -346,6 +369,7 @@ int main(int argc, char **argv)
EXPECT_EQUAL(s2n_atomic_flag_test(&client_conn->key_update_pending), false);
EXPECT_BYTEARRAY_EQUAL(client_conn->secure->client_sequence_number, zeroed_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN);
EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) > 0);
EXPECT_EQUAL(client_conn->send_key_updated, 1);

EXPECT_SUCCESS(s2n_stuffer_free(&stuffer));
EXPECT_SUCCESS(s2n_connection_free(client_conn));
Expand Down
13 changes: 13 additions & 0 deletions tls/s2n_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include <unistd.h>

#include "api/s2n.h"
/* Required for s2n_connection_get_key_update_counts */
#include "api/unstable/ktls.h"
#include "crypto/s2n_certificate.h"
#include "crypto/s2n_cipher.h"
#include "crypto/s2n_crypto.h"
Expand Down Expand Up @@ -1689,3 +1691,14 @@ S2N_RESULT s2n_connection_get_sequence_number(struct s2n_connection *conn,

return S2N_RESULT_OK;
}

int s2n_connection_get_key_update_counts(struct s2n_connection *conn,
uint8_t *send_key_updates, uint8_t *recv_key_updates)
{
POSIX_ENSURE_REF(conn);
POSIX_ENSURE_REF(send_key_updates);
POSIX_ENSURE_REF(recv_key_updates);
*send_key_updates = conn->send_key_updated;
*recv_key_updates = conn->recv_key_updated;
return S2N_SUCCESS;
}
4 changes: 4 additions & 0 deletions tls/s2n_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,10 @@ struct s2n_connection {
* The writer clears it after a KeyUpdate is sent.
*/
s2n_atomic_flag key_update_pending;

/* Track KeyUpdates for metrics */
uint8_t send_key_updated;
uint8_t recv_key_updated;
};

S2N_CLEANUP_RESULT s2n_connection_ptr_free(struct s2n_connection **s2n_connection);
Expand Down
8 changes: 8 additions & 0 deletions tls/s2n_tls13_handshake.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,21 @@ int s2n_update_application_traffic_keys(struct s2n_connection *conn, s2n_mode mo
s2n_tls13_key_blob(app_key, conn->secure->cipher_suite->record_alg->cipher->key_material_size);

/* Derives next generation of traffic key */
uint8_t *count = NULL;
POSIX_GUARD(s2n_tls13_derive_traffic_keys(&keys, &app_secret_update, &app_key, &app_iv));
if (status == RECEIVING) {
POSIX_GUARD(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(old_key, &app_key));
count = &conn->recv_key_updated;
} else {
POSIX_GUARD(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(old_key, &app_key));
count = &conn->send_key_updated;
}

/* Increment the count.
* Don't treat overflows as errors-- we only do best-effort reporting.
*/
*count = MIN(UINT8_MAX, *count + 1);

/* According to https://tools.ietf.org/html/rfc8446#section-5.3:
* Each sequence number is set to zero at the beginning of a connection and
* whenever the key is changed; the first record transmitted under a particular traffic key
Expand Down

0 comments on commit 078d1e9

Please sign in to comment.