From 0fb3769b9c3a1fd99f59916c83e17cec63a41c8a Mon Sep 17 00:00:00 2001 From: Lindsay Stewart Date: Tue, 12 Dec 2023 11:22:11 -0800 Subject: [PATCH] ktls: add TLS1.3 support (#4314) --- crypto/s2n_aead_cipher_aes_gcm.c | 123 ++++++++++++++++++++++----- crypto/s2n_ktls_crypto.h | 1 + tests/features/S2N_KTLS_SUPPORTED.c | 17 ++-- tests/unit/s2n_ktls_test.c | 18 ++-- tests/unit/s2n_self_talk_ktls_test.c | 103 ++++++++++++++++++++-- tls/s2n_ktls.c | 27 ++++-- tls/s2n_tls13_key_schedule.c | 86 ++++++++++++++----- tls/s2n_tls13_key_schedule.h | 4 + tls/s2n_tls13_secrets.c | 6 +- 9 files changed, 305 insertions(+), 80 deletions(-) diff --git a/crypto/s2n_aead_cipher_aes_gcm.c b/crypto/s2n_aead_cipher_aes_gcm.c index 0d7a8f2d848..6fea5a30080 100644 --- a/crypto/s2n_aead_cipher_aes_gcm.c +++ b/crypto/s2n_aead_cipher_aes_gcm.c @@ -377,8 +377,8 @@ static int s2n_aead_cipher_aes_gcm_destroy_key(struct s2n_session_key *key) #endif -static S2N_RESULT s2n_aead_cipher_aes128_gcm_set_ktls_info(struct s2n_ktls_crypto_info_inputs *in, - struct s2n_ktls_crypto_info *out) +static S2N_RESULT s2n_tls12_aead_cipher_aes128_gcm_set_ktls_info( + struct s2n_ktls_crypto_info_inputs *in, struct s2n_ktls_crypto_info *out) { RESULT_ENSURE_REF(in); RESULT_ENSURE_REF(out); @@ -389,29 +389,48 @@ static S2N_RESULT s2n_aead_cipher_aes128_gcm_set_ktls_info(struct s2n_ktls_crypt RESULT_ENSURE_LTE(sizeof(crypto_info->key), in->key.size); RESULT_CHECKED_MEMCPY(crypto_info->key, in->key.data, sizeof(crypto_info->key)); - - RESULT_ENSURE_LTE(sizeof(crypto_info->iv), in->iv.size); - RESULT_CHECKED_MEMCPY(crypto_info->iv, in->iv.data, sizeof(crypto_info->iv)); - RESULT_ENSURE_LTE(sizeof(crypto_info->rec_seq), in->seq.size); RESULT_CHECKED_MEMCPY(crypto_info->rec_seq, in->seq.data, sizeof(crypto_info->rec_seq)); - /* The salt is a prefix of the IV + /* TLS1.2 uses partially explicit nonces. That means that although part of the + * nonce is still fixed and implicit (the salt), the remainder is explicit + * (written into the record) and must be unique per record. The RFC5288 suggests + * using the sequence number as the explicit part. + * + * Therefore, ktls expects the salt to contain the iv derived from the secret + * and should generate the remainder of the nonce per-record. * - *= https://www.rfc-editor.org/rfc/rfc4106#section-4 - *# The salt field is a four-octet value that is assigned at the - *# beginning of the security association, and then remains constant - *# for the life of the security association. + * See the TLS1.2 RFC: + * - https://datatracker.ietf.org/doc/html/rfc5246#section-6.2.3.3 + * And RFC5288, which defines the TLS1.2 AES-GCM cipher suites: + * - https://datatracker.ietf.org/doc/html/rfc5288#section-3 */ RESULT_ENSURE_LTE(sizeof(crypto_info->salt), in->iv.size); RESULT_CHECKED_MEMCPY(crypto_info->salt, in->iv.data, sizeof(crypto_info->salt)); + /* Because TLS1.2 uses partially explicit nonces, the kernel should not + * use the iv in crypto_info but instead use a unique value for each record. + * + * As of this commit, Openssl has chosen to set the TLS1.2 IV to random + * bytes when sending and all zeroes when receiving: + * https://github.com/openssl/openssl/blob/de8e0851a1c0d22533801f081781a9f0be56c2c2/ssl/record/methods/ktls_meth.c#L197-L204 + * And GnuTLS has chosen to set the TLS1.2 IV to the sequence number: + * https://github.com/gnutls/gnutls/blob/3f42ae70a1672673cb8f27c2dd3da1a34d1cbdd7/lib/system/ktls.c#L547-L550 + * + * We (fairly arbitrarily) choose to also set it to the current sequence number. + */ + RESULT_ENSURE_LTE(sizeof(crypto_info->iv), in->seq.size); + RESULT_CHECKED_MEMCPY(crypto_info->iv, in->seq.data, sizeof(crypto_info->iv)); + RESULT_GUARD_POSIX(s2n_blob_init(&out->value, (uint8_t *) (void *) crypto_info, sizeof(s2n_ktls_crypto_info_tls12_aes_gcm_128))); return S2N_RESULT_OK; } -static S2N_RESULT s2n_aead_cipher_aes256_gcm_set_ktls_info( +/* TLS1.2 AES256 is configured like TLS1.2 AES128, but with a larger key size. + * See TLS1.2 AES128 for details (particularly a discussion of salt + iv). + */ +static S2N_RESULT s2n_tls12_aead_cipher_aes256_gcm_set_ktls_info( struct s2n_ktls_crypto_info_inputs *in, struct s2n_ktls_crypto_info *out) { RESULT_ENSURE_REF(in); @@ -423,22 +442,78 @@ static S2N_RESULT s2n_aead_cipher_aes256_gcm_set_ktls_info( RESULT_ENSURE_LTE(sizeof(crypto_info->key), in->key.size); RESULT_CHECKED_MEMCPY(crypto_info->key, in->key.data, sizeof(crypto_info->key)); + RESULT_ENSURE_LTE(sizeof(crypto_info->rec_seq), in->seq.size); + RESULT_CHECKED_MEMCPY(crypto_info->rec_seq, in->seq.data, sizeof(crypto_info->rec_seq)); + RESULT_ENSURE_LTE(sizeof(crypto_info->salt), in->iv.size); + RESULT_CHECKED_MEMCPY(crypto_info->salt, in->iv.data, sizeof(crypto_info->salt)); + RESULT_ENSURE_LTE(sizeof(crypto_info->iv), in->seq.size); + RESULT_CHECKED_MEMCPY(crypto_info->iv, in->seq.data, sizeof(crypto_info->iv)); - RESULT_ENSURE_LTE(sizeof(crypto_info->iv), in->iv.size); - RESULT_CHECKED_MEMCPY(crypto_info->iv, in->iv.data, sizeof(crypto_info->iv)); + RESULT_GUARD_POSIX(s2n_blob_init(&out->value, (uint8_t *) (void *) crypto_info, + sizeof(s2n_ktls_crypto_info_tls12_aes_gcm_256))); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_tls13_aead_cipher_aes128_gcm_set_ktls_info( + struct s2n_ktls_crypto_info_inputs *in, struct s2n_ktls_crypto_info *out) +{ + RESULT_ENSURE_REF(in); + RESULT_ENSURE_REF(out); + + s2n_ktls_crypto_info_tls12_aes_gcm_128 *crypto_info = &out->ciphers.aes_gcm_128; + crypto_info->info.version = TLS_1_3_VERSION; + crypto_info->info.cipher_type = TLS_CIPHER_AES_GCM_128; + RESULT_ENSURE_LTE(sizeof(crypto_info->key), in->key.size); + RESULT_CHECKED_MEMCPY(crypto_info->key, in->key.data, sizeof(crypto_info->key)); RESULT_ENSURE_LTE(sizeof(crypto_info->rec_seq), in->seq.size); RESULT_CHECKED_MEMCPY(crypto_info->rec_seq, in->seq.data, sizeof(crypto_info->rec_seq)); - /* The salt is a prefix of the IV + /* TLS1.3 uses fully implicit nonces. The fixed, implicit IV value derived from + * the secret is xored with the sequence number to produce a unique per-record nonce. * - *= https://www.rfc-editor.org/rfc/rfc4106#section-4 - *# The salt field is a four-octet value that is assigned at the - *# beginning of the security association, and then remains constant - *# for the life of the security association. + * See the TLS1.3 RFC: + * - https://www.rfc-editor.org/rfc/rfc8446.html#section-5.3 + * + * ktls handles this with the same structure as TLS1.2 uses for its partially + * explicit nonces by splitting the implicit IV between the salt and iv fields. */ - RESULT_ENSURE_LTE(sizeof(crypto_info->salt), in->iv.size); - RESULT_CHECKED_MEMCPY(crypto_info->salt, in->iv.data, sizeof(crypto_info->salt)); + size_t salt_size = sizeof(crypto_info->salt); + RESULT_ENSURE_LTE(salt_size, in->iv.size); + RESULT_CHECKED_MEMCPY(crypto_info->salt, in->iv.data, salt_size); + size_t iv_remainder = in->iv.size - salt_size; + RESULT_ENSURE_LTE(sizeof(crypto_info->iv), iv_remainder); + RESULT_CHECKED_MEMCPY(crypto_info->iv, in->iv.data + salt_size, sizeof(crypto_info->iv)); + + RESULT_GUARD_POSIX(s2n_blob_init(&out->value, (uint8_t *) (void *) crypto_info, + sizeof(s2n_ktls_crypto_info_tls12_aes_gcm_128))); + return S2N_RESULT_OK; +} + +/* TLS1.3 AES256 is configured like TLS1.3 AES128, but with a larger key size. + * See TLS1.3 AES128 for details (particularly a discussion of salt + iv). + */ +static S2N_RESULT s2n_tls13_aead_cipher_aes256_gcm_set_ktls_info( + struct s2n_ktls_crypto_info_inputs *in, struct s2n_ktls_crypto_info *out) +{ + RESULT_ENSURE_REF(in); + RESULT_ENSURE_REF(out); + + s2n_ktls_crypto_info_tls12_aes_gcm_256 *crypto_info = &out->ciphers.aes_gcm_256; + crypto_info->info.version = TLS_1_3_VERSION; + crypto_info->info.cipher_type = TLS_CIPHER_AES_GCM_256; + + RESULT_ENSURE_LTE(sizeof(crypto_info->key), in->key.size); + RESULT_CHECKED_MEMCPY(crypto_info->key, in->key.data, sizeof(crypto_info->key)); + RESULT_ENSURE_LTE(sizeof(crypto_info->rec_seq), in->seq.size); + RESULT_CHECKED_MEMCPY(crypto_info->rec_seq, in->seq.data, sizeof(crypto_info->rec_seq)); + + size_t salt_size = sizeof(crypto_info->salt); + RESULT_ENSURE_LTE(salt_size, in->iv.size); + RESULT_CHECKED_MEMCPY(crypto_info->salt, in->iv.data, salt_size); + size_t iv_remainder = in->iv.size - salt_size; + RESULT_ENSURE_LTE(sizeof(crypto_info->iv), iv_remainder); + RESULT_CHECKED_MEMCPY(crypto_info->iv, in->iv.data + salt_size, sizeof(crypto_info->iv)); RESULT_GUARD_POSIX(s2n_blob_init(&out->value, (uint8_t *) (void *) crypto_info, sizeof(s2n_ktls_crypto_info_tls12_aes_gcm_256))); @@ -459,7 +534,7 @@ const struct s2n_cipher s2n_aes128_gcm = { .set_encryption_key = s2n_aead_cipher_aes128_gcm_set_encryption_key, .set_decryption_key = s2n_aead_cipher_aes128_gcm_set_decryption_key, .destroy_key = s2n_aead_cipher_aes_gcm_destroy_key, - .set_ktls_info = s2n_aead_cipher_aes128_gcm_set_ktls_info, + .set_ktls_info = s2n_tls12_aead_cipher_aes128_gcm_set_ktls_info, }; const struct s2n_cipher s2n_aes256_gcm = { @@ -476,7 +551,7 @@ const struct s2n_cipher s2n_aes256_gcm = { .set_encryption_key = s2n_aead_cipher_aes256_gcm_set_encryption_key, .set_decryption_key = s2n_aead_cipher_aes256_gcm_set_decryption_key, .destroy_key = s2n_aead_cipher_aes_gcm_destroy_key, - .set_ktls_info = s2n_aead_cipher_aes256_gcm_set_ktls_info, + .set_ktls_info = s2n_tls12_aead_cipher_aes256_gcm_set_ktls_info, }; /* TLS 1.3 GCM ciphers */ @@ -494,6 +569,7 @@ const struct s2n_cipher s2n_tls13_aes128_gcm = { .set_encryption_key = s2n_aead_cipher_aes128_gcm_set_encryption_key_tls13, .set_decryption_key = s2n_aead_cipher_aes128_gcm_set_decryption_key_tls13, .destroy_key = s2n_aead_cipher_aes_gcm_destroy_key, + .set_ktls_info = s2n_tls13_aead_cipher_aes128_gcm_set_ktls_info, }; const struct s2n_cipher s2n_tls13_aes256_gcm = { @@ -510,4 +586,5 @@ const struct s2n_cipher s2n_tls13_aes256_gcm = { .set_encryption_key = s2n_aead_cipher_aes256_gcm_set_encryption_key_tls13, .set_decryption_key = s2n_aead_cipher_aes256_gcm_set_decryption_key_tls13, .destroy_key = s2n_aead_cipher_aes_gcm_destroy_key, + .set_ktls_info = s2n_tls13_aead_cipher_aes256_gcm_set_ktls_info, }; diff --git a/crypto/s2n_ktls_crypto.h b/crypto/s2n_ktls_crypto.h index b665e913feb..3fe45c167d4 100644 --- a/crypto/s2n_ktls_crypto.h +++ b/crypto/s2n_ktls_crypto.h @@ -25,6 +25,7 @@ typedef struct tls12_crypto_info_aes_gcm_256 s2n_ktls_crypto_info_tls12_aes_gcm_256; #else #define TLS_1_2_VERSION 0 + #define TLS_1_3_VERSION 0 #define TLS_CIPHER_AES_GCM_128 0 typedef struct s2n_ktls_crypto_info_stub s2n_ktls_crypto_info_tls12_aes_gcm_128; diff --git a/tests/features/S2N_KTLS_SUPPORTED.c b/tests/features/S2N_KTLS_SUPPORTED.c index 270b775f2b4..619313baf52 100644 --- a/tests/features/S2N_KTLS_SUPPORTED.c +++ b/tests/features/S2N_KTLS_SUPPORTED.c @@ -20,17 +20,18 @@ int main() { - /* Struct defined when kTLS support was added to linux + /* Initially ktls only supported AES-128 and TLS1.2: * https://github.com/torvalds/linux/blob/3c4d7559159bfe1e3b94df3a657b2cda3a34e218/include/uapi/linux/tls.h + * + * However, for simplicity our ktls probe will also require AES-256 and TLS1.3. + * If this prevents some customers from using ktls, we can split our single ktls + * feature probe into more fine-grained feature probes. */ + int versions[] = { TLS_1_2_VERSION, TLS_1_3_VERSION }; + int cipher_types[] = { TLS_CIPHER_AES_GCM_128, TLS_CIPHER_AES_GCM_256 }; + struct tls12_crypto_info_aes_gcm_128 aes_crypto_info_128 = { 0 }; - int aes_gcm_128_cipher_type = TLS_CIPHER_AES_GCM_128; struct tls12_crypto_info_aes_gcm_256 aes_crypto_info_256 = { 0 }; - int aes_gcm_256_cipher_type = TLS_CIPHER_AES_GCM_256; - struct tls_crypto_info crypto_info = { 0 }; - - int get_record_type = TLS_GET_RECORD_TYPE; - int set_record_type = TLS_SET_RECORD_TYPE; - + int operations[] = { TLS_GET_RECORD_TYPE, TLS_SET_RECORD_TYPE }; return 0; } diff --git a/tests/unit/s2n_ktls_test.c b/tests/unit/s2n_ktls_test.c index b3294332920..daa82c8ed30 100644 --- a/tests/unit/s2n_ktls_test.c +++ b/tests/unit/s2n_ktls_test.c @@ -189,14 +189,14 @@ int main(int argc, char **argv) EXPECT_EQUAL(test_key.size, sizeof(value->key)); EXPECT_BYTEARRAY_EQUAL(test_key.data, value->key, sizeof(value->key)); - EXPECT_TRUE(test_iv.size >= sizeof(value->iv)); - EXPECT_BYTEARRAY_EQUAL(test_iv.data, value->iv, sizeof(value->iv)); + EXPECT_TRUE(test_seq.size >= sizeof(value->rec_seq)); + EXPECT_BYTEARRAY_EQUAL(test_seq.data, value->rec_seq, sizeof(value->rec_seq)); EXPECT_TRUE(test_iv.size >= sizeof(value->salt)); EXPECT_BYTEARRAY_EQUAL(test_iv.data, value->salt, sizeof(value->salt)); - EXPECT_TRUE(test_seq.size >= sizeof(value->rec_seq)); - EXPECT_BYTEARRAY_EQUAL(test_seq.data, value->rec_seq, sizeof(value->rec_seq)); + EXPECT_TRUE(test_iv.size >= sizeof(value->iv)); + EXPECT_BYTEARRAY_EQUAL(test_seq.data, value->iv, sizeof(value->iv)); }; /* s2n_aes256_gcm */ @@ -221,14 +221,14 @@ int main(int argc, char **argv) EXPECT_EQUAL(test_key.size, sizeof(value->key)); EXPECT_BYTEARRAY_EQUAL(test_key.data, value->key, sizeof(value->key)); - EXPECT_TRUE(test_iv.size >= sizeof(value->iv)); - EXPECT_BYTEARRAY_EQUAL(test_iv.data, value->iv, sizeof(value->iv)); + EXPECT_TRUE(test_seq.size >= sizeof(value->rec_seq)); + EXPECT_BYTEARRAY_EQUAL(test_seq.data, value->rec_seq, sizeof(value->rec_seq)); EXPECT_TRUE(test_iv.size >= sizeof(value->salt)); EXPECT_BYTEARRAY_EQUAL(test_iv.data, value->salt, sizeof(value->salt)); - EXPECT_TRUE(test_seq.size >= sizeof(value->rec_seq)); - EXPECT_BYTEARRAY_EQUAL(test_seq.data, value->rec_seq, sizeof(value->rec_seq)); + EXPECT_TRUE(test_iv.size >= sizeof(value->iv)); + EXPECT_BYTEARRAY_EQUAL(test_seq.data, value->iv, sizeof(value->iv)); }; }; @@ -309,7 +309,7 @@ int main(int argc, char **argv) s2n_connection_ptr_free); EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn)); - server_conn->actual_protocol_version = S2N_TLS13; + server_conn->actual_protocol_version = S2N_TLS13 + 1; EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_UNSUPPORTED_CONN); server_conn->actual_protocol_version = S2N_TLS11; diff --git a/tests/unit/s2n_self_talk_ktls_test.c b/tests/unit/s2n_self_talk_ktls_test.c index 21386ab0505..4aa87c5e1ed 100644 --- a/tests/unit/s2n_self_talk_ktls_test.c +++ b/tests/unit/s2n_self_talk_ktls_test.c @@ -39,7 +39,18 @@ static S2N_RESULT s2n_setup_connections(struct s2n_connection *server, /* The test negotiate method assumes non-blocking sockets */ RESULT_GUARD_POSIX(s2n_fd_set_non_blocking(io_pair->server)); RESULT_GUARD_POSIX(s2n_fd_set_non_blocking(io_pair->client)); - RESULT_GUARD_POSIX(s2n_negotiate_test_server_and_client(server, client)); + + /* Real sockets are sometimes slow. + * Assume blocking will occur but is always recoverable. + */ + while (true) { + int result = s2n_negotiate_test_server_and_client(server, client); + if (result == S2N_SUCCESS) { + break; + } else if (s2n_errno != S2N_ERR_IO_BLOCKED) { + RESULT_GUARD_POSIX(result); + } + }; /* Our IO methods are more predictable if they use blocking sockets. */ RESULT_GUARD_POSIX(s2n_fd_set_blocking(io_pair->server)); @@ -336,6 +347,9 @@ int main(int argc, char **argv) /* Test: s2n_recv with interleaved control messages */ { + /* TLS1.3 and TLS1.2 have different expectations for control messages */ + EXPECT_EQUAL(reader->actual_protocol_version, S2N_TLS12); + const uint8_t test_record_type = TLS_CHANGE_CIPHER_SPEC; uint8_t control_record_data[] = "control record data"; struct s2n_blob control_record = { 0 }; @@ -384,6 +398,69 @@ int main(int argc, char **argv) }; }; + /* Test receiving with ktls + TLS1.3 */ + if (ktls_recv_supported && s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, "default_tls13")); + + 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(server, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, "default_tls13")); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_OK(s2n_new_inet_socket_pair(&io_pair)); + EXPECT_OK(s2n_setup_connections(server, client, &io_pair)); + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); + + struct s2n_connection *reader = client; + struct s2n_connection *writer = server; + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(reader)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Test: s2n_recv with interleaved control messages. + * + * TLS1.3 record parsing is more strict and requires actual handshake + * messages. Currently, s2n-tls does not fully parse NewSessionTicket + * handshake messages if not using tickets, so they make a good test case. + */ + { + const uint8_t test_record_type = TLS_HANDSHAKE; + uint8_t control_record_data[10 + TLS_HANDSHAKE_HEADER_LENGTH] = { + /* handshake message type */ + TLS_SERVER_NEW_SESSION_TICKET, + /* handshake message size */ + 0x00, + 0x00, + 10, + }; + struct s2n_blob control_record = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&control_record, control_record_data, + sizeof(control_record_data))); + + for (size_t i = 0; i < 5; i++) { + EXPECT_OK(s2n_record_write(writer, test_record_type, &control_record)); + EXPECT_SUCCESS(s2n_flush(writer, &blocked)); + + int written = s2n_send(writer, test_data, sizeof(test_data), &blocked); + EXPECT_EQUAL(written, sizeof(test_data)); + + uint8_t buffer[sizeof(test_data)] = { 0 }; + int read = s2n_recv(reader, buffer, sizeof(buffer), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + EXPECT_BYTEARRAY_EQUAL(test_data, buffer, read); + } + }; + }; + /* Test: s2n_shutdown * * There are three ways to trigger the read side of a TLS connection to close: @@ -504,6 +581,8 @@ int main(int argc, char **argv) } test_cases[] = { { .cipher = &s2n_aes128_gcm, .cipher_suite = &s2n_ecdhe_rsa_with_aes_128_gcm_sha256 }, { .cipher = &s2n_aes256_gcm, .cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384 }, + { .cipher = &s2n_tls13_aes128_gcm, .cipher_suite = &s2n_tls13_aes_128_gcm_sha256 }, + { .cipher = &s2n_tls13_aes256_gcm, .cipher_suite = &s2n_tls13_aes_256_gcm_sha384 }, }; /* Ensure that all supported ciphers are tested */ @@ -529,18 +608,24 @@ int main(int argc, char **argv) EXPECT_TRUE(cipher_tested); } - for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { - s2n_mode mode = modes[mode_i]; - for (size_t i = 0; i < s2n_array_len(test_cases); i++) { - struct s2n_cipher_suite *cipher_suite = test_cases[i].cipher_suite; - EXPECT_NOT_NULL(cipher_suite); - EXPECT_EQUAL(test_cases[i].cipher, cipher_suite->record_alg->cipher); + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + struct s2n_cipher_suite *cipher_suite = test_cases[i].cipher_suite; + EXPECT_NOT_NULL(cipher_suite); + EXPECT_EQUAL(test_cases[i].cipher, cipher_suite->record_alg->cipher); + + if (cipher_suite->minimum_required_tls_version >= S2N_TLS13 + && !s2n_is_tls13_fully_supported()) { + continue; + } + + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + s2n_mode mode = modes[mode_i]; struct s2n_cipher_preferences preferences = { .suites = &cipher_suite, .count = 1, }; - struct s2n_security_policy policy = *config->security_policy; + struct s2n_security_policy policy = security_policy_test_all; policy.cipher_preferences = &preferences; DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), @@ -553,7 +638,7 @@ int main(int argc, char **argv) s2n_connection_ptr_free); EXPECT_NOT_NULL(server); EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - client->security_policy_override = &policy; + server->security_policy_override = &policy; DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); EXPECT_OK(s2n_new_inet_socket_pair(&io_pair)); diff --git a/tls/s2n_ktls.c b/tls/s2n_ktls.c index 67f07c942e0..05641c86ca1 100644 --- a/tls/s2n_ktls.c +++ b/tls/s2n_ktls.c @@ -18,6 +18,7 @@ #include "crypto/s2n_ktls_crypto.h" #include "tls/s2n_prf.h" #include "tls/s2n_tls.h" +#include "tls/s2n_tls13_key_schedule.h" /* Used for overriding setsockopt calls in testing */ s2n_setsockopt_fn s2n_setsockopt = setsockopt; @@ -54,13 +55,15 @@ static S2N_RESULT s2n_ktls_validate(struct s2n_connection *conn, s2n_ktls_mode k /* kTLS enable should only be called once the handshake has completed. */ RESULT_ENSURE(is_handshake_complete(conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE); - /* TODO support TLS 1.3 + /* For now, only allow TLS1.3 for testing. * - * TLS 1.3 support requires sending the KeyUpdate message when the cryptographic - * key usage limits are met. However, this is currently only possible by applying a - * kernel patch to support this functionality. + * TLS1.3 is potentially more dangerous to enable than TLS1.2, since the kernel + * does not currently support updating TLS keys and therefore will fail if + * KeyUpdate messages are encountered. */ - RESULT_ENSURE(conn->actual_protocol_version == S2N_TLS12, S2N_ERR_KTLS_UNSUPPORTED_CONN); + if (!s2n_in_test()) { + RESULT_ENSURE(conn->actual_protocol_version == S2N_TLS12, S2N_ERR_KTLS_UNSUPPORTED_CONN); + } /* Check if the cipher supports kTLS */ const struct s2n_cipher *cipher = NULL; @@ -151,11 +154,22 @@ static S2N_RESULT s2n_ktls_crypto_info_init(struct s2n_connection *conn, s2n_ktl * "implicit_iv" when writing records, so the IV may change after generation. */ struct s2n_key_material key_material = { 0 }; - RESULT_GUARD(s2n_prf_generate_key_material(conn, &key_material)); bool is_sending_key = (ktls_mode == S2N_KTLS_MODE_SEND); s2n_mode key_mode = (is_sending_key) ? conn->mode : S2N_PEER_MODE(conn->mode); + switch (conn->actual_protocol_version) { + case S2N_TLS12: + RESULT_GUARD(s2n_prf_generate_key_material(conn, &key_material)); + break; + case S2N_TLS13: + RESULT_GUARD(s2n_tls13_key_schedule_generate_key_material( + conn, key_mode, &key_material)); + break; + default: + RESULT_BAIL(S2N_ERR_KTLS_UNSUPPORTED_CONN); + } + struct s2n_ktls_crypto_info_inputs inputs = { 0 }; if (key_mode == S2N_CLIENT) { inputs.key = key_material.client_key; @@ -173,7 +187,6 @@ static S2N_RESULT s2n_ktls_crypto_info_init(struct s2n_connection *conn, s2n_ktl RESULT_ENSURE_REF(cipher); RESULT_ENSURE_REF(cipher->set_ktls_info); RESULT_GUARD(cipher->set_ktls_info(&inputs, crypto_info)); - return S2N_RESULT_OK; } diff --git a/tls/s2n_tls13_key_schedule.c b/tls/s2n_tls13_key_schedule.c index 4d9ff1da45e..5d9f69dbd1b 100644 --- a/tls/s2n_tls13_key_schedule.c +++ b/tls/s2n_tls13_key_schedule.c @@ -13,6 +13,7 @@ * permissions and limitations under the License. */ +#include "tls/s2n_prf.h" #include "tls/s2n_tls13_handshake.h" #include "utils/s2n_result.h" @@ -41,27 +42,21 @@ static S2N_RESULT s2n_zero_sequence_number(struct s2n_connection *conn, s2n_mode return S2N_RESULT_OK; } -static S2N_RESULT s2n_set_key(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, s2n_mode mode) +static S2N_RESULT s2n_tls13_key_schedule_get_keying_material( + struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, + s2n_mode mode, struct s2n_blob *iv, struct s2n_blob *key) { RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(iv); + RESULT_ENSURE_REF(key); RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); + const struct s2n_cipher_suite *cipher_suite = conn->secure->cipher_suite; - RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg); - RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg->cipher); - const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; + RESULT_ENSURE_REF(cipher_suite); - uint8_t *implicit_iv_data = NULL; - struct s2n_session_key *session_key = NULL; - if (mode == S2N_CLIENT) { - implicit_iv_data = conn->secure->client_implicit_iv; - session_key = &conn->secure->client_key; - conn->client = conn->secure; - } else { - implicit_iv_data = conn->secure->server_implicit_iv; - session_key = &conn->secure->server_key; - conn->server = conn->secure; - } + const struct s2n_cipher *cipher = NULL; + RESULT_GUARD(s2n_connection_get_secure_cipher(conn, &cipher)); + RESULT_ENSURE_REF(cipher); /** *= https://tools.ietf.org/rfc/rfc8446#section-7.3 @@ -108,19 +103,50 @@ static S2N_RESULT s2n_set_key(struct s2n_connection *conn, s2n_extract_secret_ty *# *# [sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length) **/ - struct s2n_blob key = { 0 }; - uint8_t key_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&key, key_bytes, key_size)); + RESULT_ENSURE_LTE(key_size, key->size); + key->size = key_size; RESULT_GUARD_POSIX(s2n_hkdf_expand_label(&hmac, hmac_alg, - &secret, key_purpose, &s2n_zero_length_context, &key)); + &secret, key_purpose, &s2n_zero_length_context, key)); /** *= https://tools.ietf.org/rfc/rfc8446#section-7.3 *# [sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length) **/ - struct s2n_blob iv = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&iv, implicit_iv_data, iv_size)); + RESULT_ENSURE_LTE(iv_size, iv->size); + iv->size = iv_size; RESULT_GUARD_POSIX(s2n_hkdf_expand_label(&hmac, hmac_alg, - &secret, iv_purpose, &s2n_zero_length_context, &iv)); + &secret, iv_purpose, &s2n_zero_length_context, iv)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_set_key(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, s2n_mode mode) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->secure); + + uint8_t *implicit_iv_data = NULL; + struct s2n_session_key *session_key = NULL; + uint8_t key_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + if (mode == S2N_CLIENT) { + implicit_iv_data = conn->secure->client_implicit_iv; + session_key = &conn->secure->client_key; + conn->client = conn->secure; + } else { + implicit_iv_data = conn->secure->server_implicit_iv; + session_key = &conn->secure->server_key; + conn->server = conn->secure; + } + + struct s2n_blob iv = { 0 }; + struct s2n_blob key = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&iv, implicit_iv_data, S2N_TLS13_FIXED_IV_LEN)); + RESULT_GUARD_POSIX(s2n_blob_init(&key, key_bytes, sizeof(key_bytes))); + RESULT_GUARD(s2n_tls13_key_schedule_get_keying_material( + conn, secret_type, mode, &iv, &key)); + + const struct s2n_cipher *cipher = NULL; + RESULT_GUARD(s2n_connection_get_secure_cipher(conn, &cipher)); + RESULT_ENSURE_REF(cipher); bool is_sending_secret = (mode == conn->mode); if (is_sending_secret) { @@ -329,3 +355,17 @@ S2N_RESULT s2n_tls13_key_schedule_reset(struct s2n_connection *conn) conn->secrets.extract_secret_type = S2N_NONE_SECRET; return S2N_RESULT_OK; } + +S2N_RESULT s2n_tls13_key_schedule_generate_key_material(struct s2n_connection *conn, + s2n_mode sender, struct s2n_key_material *key_material) +{ + RESULT_GUARD(s2n_key_material_init(key_material, conn)); + if (sender == S2N_CLIENT) { + RESULT_GUARD(s2n_tls13_key_schedule_get_keying_material(conn, S2N_MASTER_SECRET, + sender, &key_material->client_iv, &key_material->client_key)); + } else { + RESULT_GUARD(s2n_tls13_key_schedule_get_keying_material(conn, S2N_MASTER_SECRET, + sender, &key_material->server_iv, &key_material->server_key)); + } + return S2N_RESULT_OK; +} diff --git a/tls/s2n_tls13_key_schedule.h b/tls/s2n_tls13_key_schedule.h index 9e0fc5e8d0c..c7270faaabc 100644 --- a/tls/s2n_tls13_key_schedule.h +++ b/tls/s2n_tls13_key_schedule.h @@ -17,5 +17,9 @@ #include "utils/s2n_result.h" +struct s2n_key_material; +S2N_RESULT s2n_tls13_key_schedule_generate_key_material(struct s2n_connection *conn, + s2n_mode sender, struct s2n_key_material *key_material); + S2N_RESULT s2n_tls13_key_schedule_update(struct s2n_connection *conn); S2N_RESULT s2n_tls13_key_schedule_reset(struct s2n_connection *conn); diff --git a/tls/s2n_tls13_secrets.c b/tls/s2n_tls13_secrets.c index 12a3b79f9fe..9bdfeb17a00 100644 --- a/tls/s2n_tls13_secrets.c +++ b/tls/s2n_tls13_secrets.c @@ -693,6 +693,11 @@ S2N_RESULT s2n_tls13_secrets_get(struct s2n_connection *conn, s2n_extract_secret RESULT_ENSURE_REF(conn->secure); RESULT_ENSURE_REF(conn->secure->cipher_suite); + bool is_available = (secret_type <= conn->secrets.extract_secret_type) + /* Unlike the other secrets, we don't wipe the master / app secrets */ + || (secret_type == S2N_MASTER_SECRET && s2n_handshake_is_complete(conn)); + RESULT_ENSURE(is_available, S2N_ERR_SAFETY); + uint8_t *secrets[][2] = { [S2N_EARLY_SECRET] = { NULL, CONN_SECRETS(conn).client_early_secret }, [S2N_HANDSHAKE_SECRET] = { CONN_SECRETS(conn).server_handshake_secret, CONN_SECRETS(conn).client_handshake_secret }, @@ -700,7 +705,6 @@ S2N_RESULT s2n_tls13_secrets_get(struct s2n_connection *conn, s2n_extract_secret }; RESULT_ENSURE_GT(secret_type, S2N_NONE_SECRET); RESULT_ENSURE_LT(secret_type, s2n_array_len(secrets)); - RESULT_ENSURE_LTE(secret_type, conn->secrets.extract_secret_type); RESULT_ENSURE_REF(secrets[secret_type][mode]); secret->size = s2n_get_hash_len(CONN_HMAC_ALG(conn));