diff --git a/tests/features/S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS.c b/tests/features/S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS.c new file mode 100644 index 00000000000..2a75230cf49 --- /dev/null +++ b/tests/features/S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS.c @@ -0,0 +1,23 @@ +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include + +int main() +{ + EVP_aead_aes_256_gcm_tls13(); + EVP_aead_aes_128_gcm_tls13(); + return 0; +} diff --git a/tests/features/S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS.flags b/tests/features/S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS.flags new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/unit/s2n_connection_serialize_test.c b/tests/unit/s2n_connection_serialize_test.c index b40a15d92b3..1f661eda220 100644 --- a/tests/unit/s2n_connection_serialize_test.c +++ b/tests/unit/s2n_connection_serialize_test.c @@ -31,16 +31,20 @@ static S2N_RESULT s2n_test_key_update(struct s2n_connection *client_conn, struct /* One side initiates key update */ RESULT_GUARD_POSIX(s2n_connection_request_key_update(client_conn, S2N_KEY_UPDATE_NOT_REQUESTED)); - /* Sending and receiving is successful after key update */ - EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); + /* Sending and receiving multiple times is successful after key update */ + for (size_t i = 0; i < 10; i++) { + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); + } EXPECT_EQUAL(client_conn->send_key_updated, 1); EXPECT_EQUAL(server_conn->recv_key_updated, 1); /* Other side initiates key update */ EXPECT_SUCCESS(s2n_connection_request_key_update(server_conn, S2N_KEY_UPDATE_NOT_REQUESTED)); - /* Sending and receiving is successful after key update */ - EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + /* Sending and receiving multiple times is successful after key update */ + for (size_t i = 0; i < 10; i++) { + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + } EXPECT_EQUAL(client_conn->recv_key_updated, 1); EXPECT_EQUAL(server_conn->send_key_updated, 1); @@ -189,6 +193,11 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server_conn), S2N_TLS12); + uint8_t iana_value[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_cipher_iana_value(server_conn, &iana_value[0], &iana_value[1])); + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_connection_serialization_length(server_conn, &length)); uint8_t buffer[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); @@ -197,9 +206,6 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_blob_init(&blob, buffer, sizeof(buffer))); struct s2n_stuffer stuffer = { 0 }; EXPECT_SUCCESS(s2n_stuffer_init_written(&stuffer, &blob)); - - uint32_t length = 0; - EXPECT_SUCCESS(s2n_connection_serialization_length(server_conn, &length)); EXPECT_EQUAL(length, s2n_stuffer_data_available(&stuffer)); uint64_t serialized_version = 0; @@ -213,8 +219,7 @@ int main(int argc, char **argv) uint8_t cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); - EXPECT_BYTEARRAY_EQUAL(cipher_suite, server_conn->secure->cipher_suite->iana_value, - S2N_TLS_CIPHER_SUITE_LEN); + EXPECT_BYTEARRAY_EQUAL(cipher_suite, iana_value, S2N_TLS_CIPHER_SUITE_LEN); uint8_t client_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, client_sequence_number, @@ -269,6 +274,11 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server_conn), S2N_TLS13); + uint8_t iana_value[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_cipher_iana_value(server_conn, &iana_value[0], &iana_value[1])); + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_connection_serialization_length(server_conn, &length)); uint8_t buffer[S2N_SERIALIZED_CONN_TLS13_SHA256_SIZE] = { 0 }; EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); @@ -277,9 +287,6 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_blob_init(&blob, buffer, sizeof(buffer))); struct s2n_stuffer stuffer = { 0 }; EXPECT_SUCCESS(s2n_stuffer_init_written(&stuffer, &blob)); - - uint32_t length = 0; - EXPECT_SUCCESS(s2n_connection_serialization_length(server_conn, &length)); EXPECT_EQUAL(length, s2n_stuffer_data_available(&stuffer)); uint64_t serialized_version = 0; @@ -293,8 +300,7 @@ int main(int argc, char **argv) uint8_t cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); - EXPECT_BYTEARRAY_EQUAL(cipher_suite, server_conn->secure->cipher_suite->iana_value, - S2N_TLS_CIPHER_SUITE_LEN); + EXPECT_BYTEARRAY_EQUAL(cipher_suite, iana_value, S2N_TLS_CIPHER_SUITE_LEN); uint8_t client_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, client_sequence_number, @@ -366,6 +372,76 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_recv(server_conn, &recv_buf, sizeof(recv_buf), &blocked)); EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); }; + + /* Cannot send or recv after serialization */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + uint8_t buffer[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + const uint8_t send_data[] = "hello world"; + EXPECT_FAILURE_WITH_ERRNO(s2n_send(server_conn, send_data, sizeof(send_data), &blocked), S2N_ERR_CLOSED); + + uint8_t recv_data = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, &recv_data, sizeof(recv_data), &blocked), S2N_ERR_CLOSED); + + /* Calling shutdown after serialization will cause a plaintext alert to be sent + * since serialization wipes the encryption context. */ + EXPECT_SUCCESS(s2n_shutdown_send(server_conn, &blocked)); + + const uint8_t expected_alert[] = { + TLS_ALERT, + S2N_TLS12 / 10, + S2N_TLS12 % 10, + 0, 2, + S2N_TLS_ALERT_LEVEL_WARNING, S2N_TLS_ALERT_CLOSE_NOTIFY + }; + uint8_t actual_alert[sizeof(expected_alert)] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&io_pair.client_in, actual_alert, sizeof(actual_alert))); + EXPECT_BYTEARRAY_EQUAL(actual_alert, expected_alert, sizeof(expected_alert)); + } + + /* Cannot serialize after connection has closed */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_shutdown_send(server_conn, &blocked)); + + /* Serializing after the connection closes will cause an error */ + uint8_t buffer[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialize(server_conn, buffer, sizeof(buffer)), S2N_ERR_CLOSED); + } }; /* s2n_connection_deserialize */ @@ -465,8 +541,10 @@ int main(int argc, char **argv) EXPECT_OK(s2n_connections_set_io_stuffer_pair(new_client_conn, server_conn, &io_pair)); /* Client can send and recv as usual */ - EXPECT_OK(s2n_send_and_recv_test(server_conn, new_client_conn)); - EXPECT_OK(s2n_send_and_recv_test(new_client_conn, server_conn)); + for (size_t idx = 0; idx < 1000; idx++) { + EXPECT_OK(s2n_send_and_recv_test(server_conn, new_client_conn)); + EXPECT_OK(s2n_send_and_recv_test(new_client_conn, server_conn)); + } }; /* Self-talk: Server can be serialized and deserialized and continue sending and receiving data @@ -507,8 +585,10 @@ int main(int argc, char **argv) EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, new_server_conn, &io_pair)); /* Server can send and recv as usual */ - EXPECT_OK(s2n_send_and_recv_test(new_server_conn, client_conn)); - EXPECT_OK(s2n_send_and_recv_test(client_conn, new_server_conn)); + for (size_t idx = 0; idx < 1000; idx++) { + EXPECT_OK(s2n_send_and_recv_test(new_server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, new_server_conn)); + } }; /* Self-talk: Test interaction between TLS1.2 session resumption and serialization */ diff --git a/tls/s2n_connection_serialize.c b/tls/s2n_connection_serialize.c index 1f959ed502c..f21088bb51e 100644 --- a/tls/s2n_connection_serialize.c +++ b/tls/s2n_connection_serialize.c @@ -15,9 +15,19 @@ #include "tls/s2n_connection_serialize.h" +#include "crypto/s2n_sequence.h" #include "tls/s2n_connection.h" #include "tls/s2n_tls13_key_schedule.h" +static bool s2n_libcrypto_supports_evp_aead_tls(void) +{ +#ifdef S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS + return true; +#else + return false; +#endif +} + int s2n_connection_serialization_length(struct s2n_connection *conn, uint32_t *length) { POSIX_ENSURE_REF(conn); @@ -85,6 +95,11 @@ int s2n_connection_serialize(struct s2n_connection *conn, uint8_t *buffer, uint3 /* This method must be called after negotiation */ POSIX_ENSURE(s2n_handshake_is_complete(conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE); + /* The connection must not be closed already. Otherwise, we might have an alert + * queued up that would be sent in cleartext after we disable encryption. */ + s2n_io_status status = S2N_IO_FULL_DUPLEX; + POSIX_ENSURE(s2n_connection_check_io_status(conn, status), S2N_ERR_CLOSED); + /* Best effort check for pending input or output data. * This method should not be called until the application has stopped sending and receiving. * Saving partial read or partial write state would complicate this problem. @@ -119,6 +134,17 @@ int s2n_connection_serialize(struct s2n_connection *conn, uint8_t *buffer, uint3 POSIX_GUARD_RESULT(s2n_connection_serialize_secrets(conn, &output)); } + /* Users should not be able to send/recv on the connection after serialization as that + * could lead to nonce reuse. We close the connection to prevent the application from sending + * more application data. However, the application could still send a close_notify alert record + * to shutdown the connection, so we also intentionally wipe keys and disable encryption. + * + * A plaintext close_notify alert is not a security concern, although the peer will likely consider + * it an error. + */ + POSIX_GUARD_RESULT(s2n_connection_set_closed(conn)); + POSIX_GUARD_RESULT(s2n_crypto_parameters_wipe(conn->secure)); + return S2N_SUCCESS; } @@ -211,6 +237,68 @@ static S2N_RESULT s2n_connection_deserialize_parse(uint8_t *buffer, uint32_t buf return S2N_RESULT_OK; } +/* Boringssl and AWS-LC do a special check in tls13 during the first call to encrypt after + * initialization. In the first call they assume that the sequence number will be 0, and therefore + * the provided nonce is equivalent to the implicit IV because 0 ^ iv = iv. The recovered implicit IV + * is stored and used later on to ensure the monotonicity of sequence numbers. + * + * In the case of deserialization, in the first call the sequence number may not be 0. + * Therefore the provided nonce cannot be considered to be the implicit IV because n ^ iv != iv. + * This inability to get the correct implicit IV causes issues with encryption later on. + * + * To resolve this we preform one throwaway encryption call with a zero sequence number after + * deserialization. This allows the libcrypto to recover the implicit IV correctly. + */ +static S2N_RESULT s2n_initialize_implicit_iv(struct s2n_connection *conn, struct s2n_connection_deserialize *parsed_values) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(parsed_values); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->server); + RESULT_ENSURE_REF(conn->client); + + if (!s2n_libcrypto_supports_evp_aead_tls()) { + return S2N_RESULT_OK; + } + + uint8_t *seq_num = parsed_values->server_sequence_number; + uint8_t *implicit_iv = conn->server->server_implicit_iv; + struct s2n_session_key key = conn->server->server_key; + if (conn->mode == S2N_CLIENT) { + seq_num = parsed_values->client_sequence_number; + implicit_iv = conn->client->client_implicit_iv; + key = conn->client->client_key; + } + + uint64_t parsed_sequence_num = 0; + struct s2n_blob seq_num_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&seq_num_blob, seq_num, S2N_TLS_SEQUENCE_NUM_LEN)); + RESULT_GUARD_POSIX(s2n_sequence_number_to_uint64(&seq_num_blob, &parsed_sequence_num)); + + /* we don't need to initialize the context when the sequence number is 0 */ + if (parsed_sequence_num == 0) { + return S2N_RESULT_OK; + } + + uint8_t in_data[S2N_TLS_GCM_TAG_LEN] = { 0 }; + struct s2n_blob in_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&in_blob, in_data, sizeof(in_data))); + + struct s2n_blob iv_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&iv_blob, implicit_iv, S2N_TLS13_FIXED_IV_LEN)); + + struct s2n_blob aad_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&aad_blob, NULL, 0)); + + RESULT_ENSURE_REF(conn->secure->cipher_suite); + RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg); + RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg->cipher); + RESULT_GUARD_POSIX(conn->secure->cipher_suite->record_alg->cipher->io.aead.encrypt(&key, + &iv_blob, &aad_blob, &in_blob, &in_blob)); + + return S2N_RESULT_OK; +} + static S2N_RESULT s2n_restore_tls13_secrets(struct s2n_connection *conn, struct s2n_connection_deserialize *parsed_values) { RESULT_ENSURE_REF(conn); @@ -226,6 +314,8 @@ static S2N_RESULT s2n_restore_tls13_secrets(struct s2n_connection *conn, struct RESULT_GUARD(s2n_tls13_key_schedule_set_key(conn, S2N_MASTER_SECRET, S2N_SERVER)); RESULT_GUARD(s2n_tls13_key_schedule_set_key(conn, S2N_MASTER_SECRET, S2N_CLIENT)); + RESULT_GUARD(s2n_initialize_implicit_iv(conn, parsed_values)); + return S2N_RESULT_OK; }