diff --git a/error/s2n_errno.c b/error/s2n_errno.c index 7b770681758..9737a4c3e85 100644 --- a/error/s2n_errno.c +++ b/error/s2n_errno.c @@ -308,6 +308,7 @@ static const char *no_such_error = "Internal s2n error"; ERR_ENTRY(S2N_ERR_KTLS_KEYUPDATE, "Received KeyUpdate from peer, but kernel does not support updating tls keys") \ ERR_ENTRY(S2N_ERR_KTLS_KEY_LIMIT, "Reached key encryption limit, but kernel does not support updating tls keys") \ ERR_ENTRY(S2N_ERR_UNEXPECTED_CERT_REQUEST, "Client does not support mutual authentication") \ + ERR_ENTRY(S2N_INVALID_SERIALIZED_CONNECTION, "Serialized connection is invalid"); \ /* clang-format on */ #define ERR_STR_CASE(ERR, str) \ diff --git a/error/s2n_errno.h b/error/s2n_errno.h index fa16387839d..57c71192541 100644 --- a/error/s2n_errno.h +++ b/error/s2n_errno.h @@ -325,6 +325,7 @@ typedef enum { S2N_ERR_ATOMIC, S2N_ERR_KTLS_KEY_LIMIT, S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT, + S2N_INVALID_SERIALIZED_CONNECTION, S2N_ERR_T_USAGE_END, } s2n_error; diff --git a/tests/testlib/s2n_test_server_client.c b/tests/testlib/s2n_test_server_client.c index f4a04e5cdb2..ab90cb0bd57 100644 --- a/tests/testlib/s2n_test_server_client.c +++ b/tests/testlib/s2n_test_server_client.c @@ -149,3 +149,24 @@ int s2n_shutdown_test_server_and_client(struct s2n_connection *server_conn, stru int rc = (server_rc == 0 && client_rc == 0) ? 0 : -1; return rc; } + +S2N_RESULT s2n_send_and_recv_test(struct s2n_connection *send_conn, struct s2n_connection *recv_conn) +{ + RESULT_ENSURE_REF(send_conn); + RESULT_ENSURE_REF(recv_conn); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + const uint8_t send_data[] = "hello world"; + ssize_t send_size = s2n_send(send_conn, send_data, sizeof(send_data), &blocked); + RESULT_GUARD_POSIX(send_size); + RESULT_ENSURE_EQ(send_size, sizeof(send_data)); + + uint8_t recv_data[sizeof(send_data)] = { 0 }; + ssize_t recv_size = s2n_recv(recv_conn, recv_data, send_size, &blocked); + RESULT_GUARD_POSIX(recv_size); + RESULT_ENSURE_EQ(recv_size, send_size); + RESULT_ENSURE_EQ(memcmp(recv_data, send_data, send_size), 0); + + return S2N_RESULT_OK; +} diff --git a/tests/testlib/s2n_testlib.h b/tests/testlib/s2n_testlib.h index 113cd2e1b5b..6cbd6178837 100644 --- a/tests/testlib/s2n_testlib.h +++ b/tests/testlib/s2n_testlib.h @@ -233,6 +233,7 @@ S2N_RESULT s2n_negotiate_test_server_and_client_until_message(struct s2n_connect int s2n_shutdown_test_server_and_client(struct s2n_connection *server_conn, struct s2n_connection *client_conn); S2N_RESULT s2n_negotiate_test_server_and_client_with_early_data(struct s2n_connection *server_conn, struct s2n_connection *client_conn, struct s2n_blob *early_data_to_send, struct s2n_blob *early_data_received); +S2N_RESULT s2n_send_and_recv_test(struct s2n_connection *send_conn, struct s2n_connection *recv_conn); /* Testing only with easily constructed contiguous data buffers could hide errors. * We should use iovecs where every buffer is allocated separately. diff --git a/tests/unit/s2n_client_hello_request_test.c b/tests/unit/s2n_client_hello_request_test.c index f1d3743b6f0..ee28287676d 100644 --- a/tests/unit/s2n_client_hello_request_test.c +++ b/tests/unit/s2n_client_hello_request_test.c @@ -24,27 +24,6 @@ static const uint8_t hello_request_msg[] = { /* empty message body */ }; -static S2N_RESULT s2n_test_send_and_recv(struct s2n_connection *send_conn, struct s2n_connection *recv_conn) -{ - RESULT_ENSURE_REF(send_conn); - RESULT_ENSURE_REF(recv_conn); - - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - - const uint8_t send_data[] = "hello world"; - ssize_t send_size = s2n_send(send_conn, send_data, sizeof(send_data), &blocked); - RESULT_GUARD_POSIX(send_size); - RESULT_ENSURE_EQ(send_size, sizeof(send_data)); - - uint8_t recv_data[sizeof(send_data)] = { 0 }; - ssize_t recv_size = s2n_recv(recv_conn, recv_data, send_size, &blocked); - RESULT_GUARD_POSIX(recv_size); - RESULT_ENSURE_EQ(recv_size, send_size); - EXPECT_BYTEARRAY_EQUAL(recv_data, send_data, send_size); - - return S2N_RESULT_OK; -} - static S2N_RESULT s2n_send_client_hello_request(struct s2n_connection *server_conn) { RESULT_ENSURE_REF(server_conn); @@ -213,16 +192,16 @@ int main(int argc, char **argv) EXPECT_TRUE(client_conn->secure_renegotiation); /* Send some data */ - EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); - EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); /* Send the hello request message. */ EXPECT_OK(s2n_send_client_hello_request(server_conn)); /* Send some more data */ for (size_t i = 0; i < 10; i++) { - EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); - EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); EXPECT_TRUE(s2n_connection_check_io_status(client_conn, S2N_IO_FULL_DUPLEX)); } }; @@ -261,8 +240,8 @@ int main(int argc, char **argv) EXPECT_OK(s2n_send_client_hello_request(server_conn)); /* no_renegotation alert NOT sent and received */ - EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); - EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); /* Callback was not set */ EXPECT_NULL(client_conn->config->renegotiate_request_cb); @@ -309,8 +288,8 @@ int main(int argc, char **argv) EXPECT_OK(s2n_send_client_hello_request(server_conn)); /* no_renegotation alert sent and received */ - EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); - EXPECT_ERROR_WITH_ERRNO(s2n_test_send_and_recv(client_conn, server_conn), S2N_ERR_ALERT); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_ERROR_WITH_ERRNO(s2n_send_and_recv_test(client_conn, server_conn), S2N_ERR_ALERT); EXPECT_EQUAL(s2n_connection_get_alert(server_conn), S2N_TLS_ALERT_NO_RENEGOTIATION); /* Callback triggered */ @@ -353,8 +332,8 @@ int main(int argc, char **argv) EXPECT_OK(s2n_send_client_hello_request(server_conn)); /* no_renegotation alert NOT sent and received */ - EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); - EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); /* Callback triggered */ EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb); @@ -391,8 +370,8 @@ int main(int argc, char **argv) EXPECT_OK(s2n_send_client_hello_request(server_conn)); /* no_renegotation alert NOT sent and received */ - EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); - EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); /* Callback triggered */ EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb); @@ -445,8 +424,8 @@ int main(int argc, char **argv) EXPECT_OK(s2n_send_client_hello_request(server_conn)); /* no_renegotation alert sent and received */ - EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); - EXPECT_ERROR_WITH_ERRNO(s2n_test_send_and_recv(client_conn, server_conn), S2N_ERR_ALERT); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_ERROR_WITH_ERRNO(s2n_send_and_recv_test(client_conn, server_conn), S2N_ERR_ALERT); EXPECT_EQUAL(s2n_connection_get_alert(server_conn), S2N_TLS_ALERT_NO_RENEGOTIATION); /* Callback was not triggered */ @@ -491,7 +470,7 @@ int main(int argc, char **argv) * Applications won't be able to set s2n_errno to a meaningful value, * so we need to set it to S2N_ERR_CANCELED for them. */ - EXPECT_ERROR_WITH_ERRNO(s2n_test_send_and_recv(server_conn, client_conn), S2N_ERR_CANCELLED); + EXPECT_ERROR_WITH_ERRNO(s2n_send_and_recv_test(server_conn, client_conn), S2N_ERR_CANCELLED); }; /* Test: SSLv3 sends a fatal handshake_failure alert instead of no_renegotiate diff --git a/tests/unit/s2n_connection_serialize_test.c b/tests/unit/s2n_connection_serialize_test.c new file mode 100644 index 00000000000..df0d6aa92c0 --- /dev/null +++ b/tests/unit/s2n_connection_serialize_test.c @@ -0,0 +1,478 @@ +/* + * 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 "tls/s2n_connection_serialize.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_config.h" + +#define S2N_SERIALIZED_CONN_TLS13_SHA256_SIZE 126 + +#define TEST_SEQUENCE_NUM 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +#define TEST_TLS13_SECRET 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *tls12_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls12_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(tls12_config)); + EXPECT_SUCCESS(s2n_config_set_serialized_connection_version(tls12_config, S2N_SERIALIZED_CONN_V1)); + + DEFER_CLEANUP(struct s2n_config *tls13_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls13_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(tls13_config)); + EXPECT_SUCCESS(s2n_config_set_serialized_connection_version(tls13_config, S2N_SERIALIZED_CONN_V1)); + /* Security policy that can negotiate TLS13 and has aes_128_gcm_sha256 as its preferred cipher suite */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_config, "20210825")); + + /* s2n_connection_serialization_length */ + { + /* Safety */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + uint32_t length = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialization_length(conn, NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialization_length(NULL, &length), S2N_ERR_NULL); + }; + + /* Length is correct for all possible cipher suites in TLS1.3 */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, tls13_config)); + conn->actual_protocol_version = S2N_TLS13; + + for (size_t i = 0; i < cipher_preferences_test_all.count; i++) { + struct s2n_cipher_suite *cipher_suite = cipher_preferences_test_all.suites[i]; + conn->secure->cipher_suite = cipher_suite; + uint8_t expected_secret_size = 0; + EXPECT_SUCCESS(s2n_hmac_digest_size(cipher_suite->prf_alg, &expected_secret_size)); + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_connection_serialization_length(conn, &length)); + EXPECT_EQUAL(length, S2N_SERIALIZED_CONN_FIXED_SIZE + (expected_secret_size * 3)); + } + }; + + /* Length is correct for TLS1.2 */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, tls12_config)); + conn->actual_protocol_version = S2N_TLS12; + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_connection_serialization_length(conn, &length)); + EXPECT_EQUAL(length, S2N_SERIALIZED_CONN_TLS12_SIZE); + }; + }; + + /* s2n_connection_serialize */ + { + /* Safety */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + uint8_t data = 0; + uint32_t length = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialize(NULL, &data, length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialize(conn, NULL, length), S2N_ERR_NULL); + }; + + /* Invalid usage checks */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint8_t buffer[10] = { 0 }; + uint32_t length = sizeof(buffer); + + /* Format version must be set before calling this function */ + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialize(conn, buffer, length), + S2N_ERR_INVALID_STATE); + + /* Negotiation must be complete before calling this function */ + EXPECT_SUCCESS(s2n_config_set_serialized_connection_version(config, S2N_SERIALIZED_CONN_V1)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialize(conn, buffer, length), + S2N_ERR_HANDSHAKE_NOT_COMPLETE); + + /* Buffer must be large enough to hold entire serialized length */ + EXPECT_OK(s2n_skip_handshake(conn)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialize(conn, buffer, length), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + }; + + /* Serializes TLS 1.2 */ + { + 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, tls12_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_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)); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server_conn), S2N_TLS12); + + uint8_t buffer[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); + + struct s2n_blob blob = { 0 }; + 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; + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&stuffer, &serialized_version)); + EXPECT_EQUAL(serialized_version, S2N_SERIALIZED_CONN_V1); + + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, protocol_version, + S2N_TLS_PROTOCOL_VERSION_LEN)); + EXPECT_EQUAL((protocol_version[0] * 10) + protocol_version[1], S2N_TLS12); + + 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); + + uint8_t client_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, client_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN)); + uint8_t expected_sequence_number[] = { 0, 0, 0, 0, 0, 0, 0, 1 }; + EXPECT_BYTEARRAY_EQUAL(client_sequence_number, expected_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN); + + uint8_t server_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, server_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN)); + EXPECT_BYTEARRAY_EQUAL(server_sequence_number, expected_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN); + + uint16_t frag_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &frag_len)); + EXPECT_EQUAL(frag_len, S2N_DEFAULT_FRAGMENT_LENGTH); + + uint8_t master_secret[S2N_TLS_SECRET_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, master_secret, S2N_TLS_SECRET_LEN)); + EXPECT_BYTEARRAY_EQUAL(master_secret, server_conn->secrets.version.tls12.master_secret, + S2N_TLS_SECRET_LEN); + + uint8_t client_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, client_random, S2N_TLS_RANDOM_DATA_LEN)); + EXPECT_BYTEARRAY_EQUAL(client_random, server_conn->handshake_params.client_random, + S2N_TLS_RANDOM_DATA_LEN); + + uint8_t server_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, server_random, S2N_TLS_RANDOM_DATA_LEN)); + EXPECT_BYTEARRAY_EQUAL(server_random, server_conn->handshake_params.server_random, + S2N_TLS_RANDOM_DATA_LEN); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + }; + + /* Serializes TLS 1.3 */ + if (s2n_is_tls13_fully_supported()) { + 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)); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server_conn), S2N_TLS13); + + uint8_t buffer[S2N_SERIALIZED_CONN_TLS13_SHA256_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); + + struct s2n_blob blob = { 0 }; + 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; + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&stuffer, &serialized_version)); + EXPECT_EQUAL(serialized_version, S2N_SERIALIZED_CONN_V1); + + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, protocol_version, + S2N_TLS_PROTOCOL_VERSION_LEN)); + EXPECT_EQUAL((protocol_version[0] * 10) + protocol_version[1], S2N_TLS13); + + 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); + + uint8_t client_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, client_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN)); + uint8_t expected_sequence_number[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + EXPECT_BYTEARRAY_EQUAL(client_sequence_number, expected_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN); + + uint8_t server_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, server_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN)); + EXPECT_BYTEARRAY_EQUAL(server_sequence_number, expected_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN); + + uint16_t frag_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &frag_len)); + EXPECT_EQUAL(frag_len, S2N_DEFAULT_FRAGMENT_LENGTH); + + uint8_t client_secret[SHA256_DIGEST_LENGTH] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, client_secret, SHA256_DIGEST_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(client_secret, server_conn->secrets.version.tls13.client_app_secret, + SHA256_DIGEST_LENGTH); + + uint8_t server_secret[SHA256_DIGEST_LENGTH] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, server_secret, SHA256_DIGEST_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(server_secret, server_conn->secrets.version.tls13.server_app_secret, + SHA256_DIGEST_LENGTH); + + uint8_t resumption_secret[SHA256_DIGEST_LENGTH] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, resumption_secret, SHA256_DIGEST_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(resumption_secret, server_conn->secrets.version.tls13.resumption_master_secret, + SHA256_DIGEST_LENGTH); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + }; + + /* IO buffers must be empty before calling this function */ + { + 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, tls12_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_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 data[] = "Hello"; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_send(client_conn, data, sizeof(data), &blocked)); + + /* Partial read so that some data remains in the buffer */ + uint8_t recv_buf[10] = { 0 }; + EXPECT_SUCCESS(s2n_recv(server_conn, &recv_buf, 1, &blocked)); + + uint8_t buffer[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialize(server_conn, buffer, + sizeof(buffer)), + S2N_ERR_INVALID_STATE); + + /* Finish reading to successfully get the serialized connection */ + EXPECT_SUCCESS(s2n_recv(server_conn, &recv_buf, sizeof(recv_buf), &blocked)); + EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); + }; + }; + + /* s2n_connection_deserialize */ + { + /* Safety */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + uint8_t buffer = 0; + uint32_t length = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_deserialize(NULL, &buffer, length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_deserialize(conn, NULL, length), S2N_ERR_NULL); + }; + + /* Errors if format version is unknown */ + { + uint8_t test_context[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, /* Unknown serialized_connection version */ + 0x03, 0x04, /* TLS 1.3 */ + TLS_AES_128_GCM_SHA256, + TEST_SEQUENCE_NUM, /* Client sequence num */ + TEST_SEQUENCE_NUM, /* Server sequence num */ + 0x01, 0x01, /* Test Fragment length */ + TEST_TLS13_SECRET, /* Client app secret */ + TEST_TLS13_SECRET, /* Server app secret */ + TEST_TLS13_SECRET /* Resumption master secret */ + }; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_deserialize(client_conn, test_context, + sizeof(test_context)), + S2N_INVALID_SERIALIZED_CONNECTION); + }; + + /* Succeeds if format version is known */ + { + uint8_t test_context[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, S2N_SERIALIZED_CONN_V1, + 0x03, 0x04, /* TLS 1.3 */ + TLS_AES_128_GCM_SHA256, + TEST_SEQUENCE_NUM, /* Client sequence num */ + TEST_SEQUENCE_NUM, /* Server sequence num */ + 0x01, 0x01, /* Test Fragment length */ + TEST_TLS13_SECRET, /* Client app secret */ + TEST_TLS13_SECRET, /* Server app secret */ + TEST_TLS13_SECRET /* Resumption master secret */ + }; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_deserialize(client_conn, test_context, + sizeof(test_context))); + }; + }; + + struct s2n_config *config_array[] = { tls12_config, tls13_config }; + + /* Self-talk: Client can be serialized and deserialized and continue sending and receiving data + * in TLS1.2 and TLS1.3 */ + for (size_t i = 0; i < s2n_array_len(config_array); i++) { + 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, config_array[i])); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_array[i])); + + 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)); + + /* Preliminary send and receive */ + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); + + uint8_t buffer[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_serialize(client_conn, buffer, sizeof(buffer))); + + /* Initialize new client connection and deserialize the connection */ + DEFER_CLEANUP(struct s2n_connection *new_client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(new_client_conn); + EXPECT_SUCCESS(s2n_connection_deserialize(new_client_conn, buffer, sizeof(buffer))); + + /* Wipe and re-initialize IO pipes */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&io_pair.client_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&io_pair.server_in)); + 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)); + }; + + /* Self-talk: Server can be serialized and deserialized and continue sending and receiving data + * in TLS1.2 and TLS1.3 */ + for (size_t i = 0; i < s2n_array_len(config_array); i++) { + 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, config_array[i])); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_array[i])); + + 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)); + + /* Preliminary send and receive */ + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); + + uint8_t buffer[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); + + /* Initialize new server connection and deserialize the connection */ + DEFER_CLEANUP(struct s2n_connection *new_server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(new_server_conn); + EXPECT_SUCCESS(s2n_connection_deserialize(new_server_conn, buffer, sizeof(buffer))); + + /* Wipe and re-initialize IO pipes */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&io_pair.client_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&io_pair.server_in)); + 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)); + }; + + END_TEST(); + return 0; +} diff --git a/tls/s2n_config.c b/tls/s2n_config.c index aea8689a217..366e2b6bd45 100644 --- a/tls/s2n_config.c +++ b/tls/s2n_config.c @@ -1219,3 +1219,14 @@ int s2n_config_get_supported_groups(struct s2n_config *config, uint16_t *groups, return S2N_SUCCESS; } + +int s2n_config_set_serialized_connection_version(struct s2n_config *config, s2n_serialization_version version) +{ + POSIX_ENSURE_REF(config); + + /* Currently there is only one format version supported */ + POSIX_ENSURE_EQ(version, S2N_SERIALIZED_CONN_V1); + config->serialized_connection_version = version; + + return S2N_SUCCESS; +} diff --git a/tls/s2n_config.h b/tls/s2n_config.h index 10da5ce8f1a..9880dfab587 100644 --- a/tls/s2n_config.h +++ b/tls/s2n_config.h @@ -54,6 +54,11 @@ typedef enum { S2N_LIB_OWNED, } s2n_cert_ownership; +typedef enum { + S2N_SERIALIZED_CONN_NONE = 0, + S2N_SERIALIZED_CONN_V1 = 1 +} s2n_serialization_version; + struct s2n_config { unsigned use_tickets : 1; @@ -208,6 +213,23 @@ struct s2n_config { void *renegotiate_request_ctx; s2n_renegotiate_request_cb renegotiate_request_cb; + + /* This version is meant as a safeguard against future TLS features which might affect the connection + * serialization feature. + * + * For example, suppose that a new TLS parameter is released which affects how data is sent + * post-handshake. This parameter must be available in both the s2n-tls version that serializes the + * connection, as well as the version that deserializes the connection. If not, the serializer + * may negotiate this feature with its peer, which would cause an older deserializer to run into errors + * sending data to the peer. + * + * This kind of version-mismatch can happen during deployments and rollbacks, and therefore we require + * the user to tell us which serialized version they support pre-handshake. + * We will not negotiate a new feature until the user requests the serialized connection + * version the feature is tied to (i.e. the request indicates they have finished deploying + * the new feature to their entire fleet.) + */ + s2n_serialization_version serialized_connection_version; }; S2N_CLEANUP_RESULT s2n_config_ptr_free(struct s2n_config **config); @@ -229,3 +251,6 @@ S2N_RESULT s2n_config_wall_clock(struct s2n_config *config, uint64_t *output); * in `security_policy` */ S2N_RESULT s2n_config_validate_loaded_certificates(const struct s2n_config *config, const struct s2n_security_policy *security_policy); + +/* APIs that will be moved to s2n.h when the serialized connection feature is released */ +int s2n_config_set_serialized_connection_version(struct s2n_config *config, s2n_serialization_version version); diff --git a/tls/s2n_connection.h b/tls/s2n_connection.h index 44f3e892345..8c85ac9783c 100644 --- a/tls/s2n_connection.h +++ b/tls/s2n_connection.h @@ -133,6 +133,10 @@ struct s2n_connection { /* Indicates whether the connection should request OCSP stapling from the peer */ unsigned request_ocsp_status : 1; + /* Indicates that the connection was created from deserialization + * and therefore knowledge of the original handshake is limited. */ + unsigned deserialized_conn : 1; + /* The configuration (cert, key .. etc ) */ struct s2n_config *config; diff --git a/tls/s2n_connection_serialize.c b/tls/s2n_connection_serialize.c new file mode 100644 index 00000000000..4d97ab4b564 --- /dev/null +++ b/tls/s2n_connection_serialize.c @@ -0,0 +1,285 @@ +/* + * 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 "tls/s2n_connection_serialize.h" + +#include "tls/s2n_connection.h" +#include "tls/s2n_tls13_key_schedule.h" + +int s2n_connection_serialization_length(struct s2n_connection *conn, uint32_t *length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + POSIX_ENSURE_REF(length); + + POSIX_ENSURE(conn->config->serialized_connection_version != S2N_SERIALIZED_CONN_NONE, + S2N_ERR_INVALID_STATE); + + if (conn->actual_protocol_version >= S2N_TLS13) { + uint8_t secret_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size)); + *length = S2N_SERIALIZED_CONN_FIXED_SIZE + (secret_size * 3); + } else { + *length = S2N_SERIALIZED_CONN_TLS12_SIZE; + } + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_connection_serialize_tls13_secrets(struct s2n_connection *conn, struct s2n_stuffer *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + + uint8_t secret_size = 0; + RESULT_GUARD_POSIX(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size)); + + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, conn->secrets.version.tls13.client_app_secret, + secret_size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, conn->secrets.version.tls13.server_app_secret, + secret_size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, conn->secrets.version.tls13.resumption_master_secret, + secret_size)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_connection_serialize_secrets(struct s2n_connection *conn, struct s2n_stuffer *output) +{ + RESULT_ENSURE_REF(conn); + + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, conn->secrets.version.tls12.master_secret, + S2N_TLS_SECRET_LEN)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, conn->handshake_params.client_random, + S2N_TLS_RANDOM_DATA_LEN)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, conn->handshake_params.server_random, + S2N_TLS_RANDOM_DATA_LEN)); + return S2N_RESULT_OK; +} + +int s2n_connection_serialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_REF(buffer); + + POSIX_ENSURE(conn->config->serialized_connection_version != S2N_SERIALIZED_CONN_NONE, + S2N_ERR_INVALID_STATE); + + /* This method must be called after negotiation */ + POSIX_ENSURE(s2n_handshake_is_complete(conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE); + + /* 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. + */ + POSIX_ENSURE(s2n_stuffer_data_available(&conn->header_in) == 0, S2N_ERR_INVALID_STATE); + POSIX_ENSURE(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_INVALID_STATE); + POSIX_ENSURE(s2n_stuffer_data_available(&conn->out) == 0, S2N_ERR_INVALID_STATE); + + uint32_t context_length = 0; + POSIX_GUARD(s2n_connection_serialization_length(conn, &context_length)); + POSIX_ENSURE(buffer_length >= context_length, S2N_ERR_INSUFFICIENT_MEM_SIZE); + + struct s2n_blob context_blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&context_blob, buffer, buffer_length)); + struct s2n_stuffer output = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&output, &context_blob)); + + POSIX_GUARD(s2n_stuffer_write_uint64(&output, S2N_SERIALIZED_CONN_V1)); + + POSIX_GUARD(s2n_stuffer_write_uint8(&output, conn->actual_protocol_version / 10)); + POSIX_GUARD(s2n_stuffer_write_uint8(&output, conn->actual_protocol_version % 10)); + POSIX_GUARD(s2n_stuffer_write_bytes(&output, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); + + POSIX_GUARD(s2n_stuffer_write_bytes(&output, conn->secure->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + POSIX_GUARD(s2n_stuffer_write_bytes(&output, conn->secure->server_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + + POSIX_GUARD(s2n_stuffer_write_uint16(&output, conn->max_outgoing_fragment_length)); + + if (conn->actual_protocol_version >= S2N_TLS13) { + POSIX_GUARD_RESULT(s2n_connection_serialize_tls13_secrets(conn, &output)); + } else { + POSIX_GUARD_RESULT(s2n_connection_serialize_secrets(conn, &output)); + } + + return S2N_SUCCESS; +} + +struct s2n_connection_deserialize { + uint8_t protocol_version; + struct s2n_cipher_suite *cipher_suite; + uint8_t client_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN]; + uint8_t server_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN]; + uint16_t max_fragment_len; + union { + struct { + uint8_t master_secret[S2N_TLS_SECRET_LEN]; + uint8_t client_random[S2N_TLS_RANDOM_DATA_LEN]; + uint8_t server_random[S2N_TLS_RANDOM_DATA_LEN]; + } tls12; + struct { + uint8_t secret_size; + uint8_t client_application_secret[S2N_TLS_SECRET_LEN]; + uint8_t server_application_secret[S2N_TLS_SECRET_LEN]; + uint8_t resumption_master_secret[S2N_TLS_SECRET_LEN]; + } tls13; + } version; +}; + +static S2N_RESULT s2n_connection_deserialize_tls13_secrets(struct s2n_stuffer *input, + struct s2n_connection_deserialize *parsed_values) +{ + RESULT_ENSURE_REF(input); + RESULT_ENSURE_REF(parsed_values); + + RESULT_GUARD_POSIX(s2n_hmac_digest_size(parsed_values->cipher_suite->prf_alg, + &parsed_values->version.tls13.secret_size)); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, parsed_values->version.tls13.client_application_secret, + parsed_values->version.tls13.secret_size)); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, parsed_values->version.tls13.server_application_secret, + parsed_values->version.tls13.secret_size)); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, parsed_values->version.tls13.resumption_master_secret, + parsed_values->version.tls13.secret_size)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_connection_deserialize_secrets(struct s2n_stuffer *input, + struct s2n_connection_deserialize *parsed_values) +{ + RESULT_ENSURE_REF(input); + RESULT_ENSURE_REF(parsed_values); + + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, parsed_values->version.tls12.master_secret, S2N_TLS_SECRET_LEN)); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, parsed_values->version.tls12.client_random, S2N_TLS_RANDOM_DATA_LEN)); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, parsed_values->version.tls12.server_random, S2N_TLS_RANDOM_DATA_LEN)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_connection_deserialize_parse(uint8_t *buffer, uint32_t buffer_length, + struct s2n_connection_deserialize *parsed_values) +{ + RESULT_ENSURE_REF(parsed_values); + + struct s2n_blob context_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&context_blob, buffer, buffer_length)); + struct s2n_stuffer input = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&input, &context_blob)); + + uint64_t serialized_version = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(&input, &serialized_version)); + /* No other version is supported currently */ + RESULT_ENSURE_EQ(serialized_version, S2N_SERIALIZED_CONN_V1); + + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(&input, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + parsed_values->protocol_version = (protocol_version[0] * 10) + protocol_version[1]; + + uint8_t cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(&input, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); + RESULT_GUARD(s2n_cipher_suite_from_iana(cipher_suite, S2N_TLS_CIPHER_SUITE_LEN, &parsed_values->cipher_suite)); + + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(&input, parsed_values->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(&input, parsed_values->server_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&input, &parsed_values->max_fragment_len)); + + if (parsed_values->protocol_version >= S2N_TLS13) { + RESULT_GUARD(s2n_connection_deserialize_tls13_secrets(&input, parsed_values)); + } else { + RESULT_GUARD(s2n_connection_deserialize_secrets(&input, parsed_values)); + } + + 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); + RESULT_ENSURE_REF(parsed_values); + + RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.client_app_secret, + parsed_values->version.tls13.client_application_secret, parsed_values->version.tls13.secret_size); + RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.server_app_secret, + parsed_values->version.tls13.server_application_secret, parsed_values->version.tls13.secret_size); + RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.resumption_master_secret, + parsed_values->version.tls13.resumption_master_secret, parsed_values->version.tls13.secret_size); + + 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)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_restore_secrets(struct s2n_connection *conn, struct s2n_connection_deserialize *parsed_values) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(parsed_values); + + RESULT_CHECKED_MEMCPY(conn->secrets.version.tls12.master_secret, parsed_values->version.tls12.master_secret, + S2N_TLS_SECRET_LEN); + RESULT_CHECKED_MEMCPY(conn->handshake_params.client_random, parsed_values->version.tls12.client_random, + S2N_TLS_RANDOM_DATA_LEN); + RESULT_CHECKED_MEMCPY(conn->handshake_params.server_random, parsed_values->version.tls12.server_random, + S2N_TLS_RANDOM_DATA_LEN); + RESULT_GUARD_POSIX(s2n_prf_key_expansion(conn)); + + return S2N_RESULT_OK; +} + +int s2n_connection_deserialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(buffer); + + /* Read parsed values into a temporary struct so that the connection is unaltered if parsing fails */ + struct s2n_connection_deserialize parsed_values = { 0 }; + POSIX_ENSURE(s2n_result_is_ok(s2n_connection_deserialize_parse(buffer, buffer_length, &parsed_values)), + S2N_INVALID_SERIALIZED_CONNECTION); + + /* Rehydrate fields now that parsing has completed successfully */ + conn->actual_protocol_version = parsed_values.protocol_version; + conn->secure->cipher_suite = parsed_values.cipher_suite; + POSIX_GUARD_RESULT(s2n_connection_set_max_fragment_length(conn, parsed_values.max_fragment_len)); + + /* Mark the connection as having been deserialized */ + conn->deserialized_conn = true; + + /* Key expansion */ + if (parsed_values.protocol_version >= S2N_TLS13) { + POSIX_GUARD_RESULT(s2n_restore_tls13_secrets(conn, &parsed_values)); + } else { + POSIX_GUARD_RESULT(s2n_restore_secrets(conn, &parsed_values)); + } + + /* Wait until after key generation to restore sequence numbers since they get zeroed during + * key expansion */ + POSIX_CHECKED_MEMCPY(conn->secure->client_sequence_number, parsed_values.client_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN); + POSIX_CHECKED_MEMCPY(conn->secure->server_sequence_number, parsed_values.server_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN); + + conn->client = conn->secure; + conn->server = conn->secure; + + return S2N_SUCCESS; +} diff --git a/tls/s2n_connection_serialize.h b/tls/s2n_connection_serialize.h new file mode 100644 index 00000000000..e7b4ae21f55 --- /dev/null +++ b/tls/s2n_connection_serialize.h @@ -0,0 +1,30 @@ +/* + * 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 + +#include "tls/s2n_connection.h" + +#pragma once + +#define S2N_SERIALIZED_CONN_FIXED_SIZE (8 + S2N_TLS_PROTOCOL_VERSION_LEN + S2N_TLS_CIPHER_SUITE_LEN \ + + S2N_TLS_SEQUENCE_NUM_LEN + S2N_TLS_SEQUENCE_NUM_LEN + 2) +#define S2N_SERIALIZED_CONN_TLS12_SIZE (S2N_SERIALIZED_CONN_FIXED_SIZE + S2N_TLS_SECRET_LEN \ + + S2N_TLS_RANDOM_DATA_LEN + S2N_TLS_RANDOM_DATA_LEN) + +/* APIs that will be moved to s2n.h when the connection serialize feature is released */ +int s2n_connection_serialization_length(struct s2n_connection *conn, uint32_t *length); +int s2n_connection_serialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length); +int s2n_connection_deserialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length); diff --git a/tls/s2n_handshake_io.c b/tls/s2n_handshake_io.c index 514c671b281..f6ebe3e3c86 100644 --- a/tls/s2n_handshake_io.c +++ b/tls/s2n_handshake_io.c @@ -1591,7 +1591,9 @@ static int s2n_handle_retry_state(struct s2n_connection *conn) bool s2n_handshake_is_complete(struct s2n_connection *conn) { - return conn && ACTIVE_STATE(conn).writer == 'B'; + /* A deserialized connection implies that the handshake is complete because + * connections cannot be serialized before completing the handshake. */ + return conn && (ACTIVE_STATE(conn).writer == 'B' || conn->deserialized_conn); } int s2n_negotiate_impl(struct s2n_connection *conn, s2n_blocked_status *blocked) diff --git a/tls/s2n_record_write.c b/tls/s2n_record_write.c index f0455c8842d..6057c199b43 100644 --- a/tls/s2n_record_write.c +++ b/tls/s2n_record_write.c @@ -32,6 +32,11 @@ extern uint8_t s2n_unknown_protocol_version; +/* In TLS1.3 the record type is obfuscated as APPLICATION_DATA once the handshake begins to be encrypted. + * The real record type is encrypted and written in the final byte of the record. + * In TLS1.2 the record type is always cleartext. */ +#define RECORD_TYPE(is_tls13_record, content_type) (is_tls13_record ? TLS_APPLICATION_DATA : content_type) + /* How much overhead does the IV, MAC, TAG and padding bytes introduce ? */ static S2N_RESULT s2n_tls_record_overhead(struct s2n_connection *conn, uint16_t *out) { @@ -155,7 +160,7 @@ S2N_RESULT s2n_record_min_write_payload_size(struct s2n_connection *conn, uint16 return S2N_RESULT_OK; } -int s2n_record_write_protocol_version(struct s2n_connection *conn, struct s2n_stuffer *out) +int s2n_record_write_protocol_version(struct s2n_connection *conn, uint8_t record_type, struct s2n_stuffer *out) { uint8_t record_protocol_version = conn->actual_protocol_version; @@ -170,11 +175,13 @@ int s2n_record_write_protocol_version(struct s2n_connection *conn, struct s2n_st * use that assumed value here in case we are talking to a legacy * server that expects TLS1.0. * - * If we are requesting early data, we can assume that we aren't talking to - * a legacy server as a legacy server would not know how to handle early data. + * Both TLS 1.3 early data and a deserialized connection will + * send data without the server_protocol_version being known. However, + * the record type would be set to APPLICATION_DATA in their cases + * so this check is avoided. **/ if (conn->server_protocol_version == s2n_unknown_protocol_version - && conn->early_data_state != S2N_EARLY_DATA_REQUESTED) { + && record_type == TLS_HANDSHAKE) { record_protocol_version = MIN(record_protocol_version, S2N_TLS10); } @@ -352,10 +359,9 @@ int s2n_record_writev(struct s2n_connection *conn, uint8_t content_type, const s POSIX_GUARD(s2n_stuffer_init(&record_stuffer, &record_blob)); /* Now that we know the length, start writing the record */ - POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, is_tls13_record ? - /* tls 1.3 opaque type */ TLS_APPLICATION_DATA : - /* actual content_type */ content_type)); - POSIX_GUARD(s2n_record_write_protocol_version(conn, &record_stuffer)); + uint8_t record_type = RECORD_TYPE(is_tls13_record, content_type); + POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, record_type)); + POSIX_GUARD(s2n_record_write_protocol_version(conn, record_type, &record_stuffer)); /* First write a header that has the payload length, this is for the MAC */ POSIX_GUARD(s2n_stuffer_write_uint16(&record_stuffer, data_bytes_to_take)); diff --git a/tls/s2n_tls13_key_schedule.c b/tls/s2n_tls13_key_schedule.c index 5d9f69dbd1b..e86660874a0 100644 --- a/tls/s2n_tls13_key_schedule.c +++ b/tls/s2n_tls13_key_schedule.c @@ -27,8 +27,8 @@ *# The notation "K_{send,recv} = foo" means "set *# the send/recv key to the given key". */ -#define K_send(conn, secret_type) RESULT_GUARD(s2n_set_key(conn, secret_type, (conn)->mode)) -#define K_recv(conn, secret_type) RESULT_GUARD(s2n_set_key(conn, secret_type, S2N_PEER_MODE((conn)->mode))) +#define K_send(conn, secret_type) RESULT_GUARD(s2n_tls13_key_schedule_set_key(conn, secret_type, (conn)->mode)) +#define K_recv(conn, secret_type) RESULT_GUARD(s2n_tls13_key_schedule_set_key(conn, secret_type, S2N_PEER_MODE((conn)->mode))) static const struct s2n_blob s2n_zero_length_context = { 0 }; @@ -119,7 +119,7 @@ static S2N_RESULT s2n_tls13_key_schedule_get_keying_material( return S2N_RESULT_OK; } -static S2N_RESULT s2n_set_key(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, s2n_mode mode) +S2N_RESULT s2n_tls13_key_schedule_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); diff --git a/tls/s2n_tls13_key_schedule.h b/tls/s2n_tls13_key_schedule.h index c7270faaabc..13dac5a25c4 100644 --- a/tls/s2n_tls13_key_schedule.h +++ b/tls/s2n_tls13_key_schedule.h @@ -15,6 +15,7 @@ #pragma once +#include "tls/s2n_tls13_secrets.h" #include "utils/s2n_result.h" struct s2n_key_material; @@ -23,3 +24,4 @@ S2N_RESULT s2n_tls13_key_schedule_generate_key_material(struct s2n_connection *c S2N_RESULT s2n_tls13_key_schedule_update(struct s2n_connection *conn); S2N_RESULT s2n_tls13_key_schedule_reset(struct s2n_connection *conn); +S2N_RESULT s2n_tls13_key_schedule_set_key(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, s2n_mode mode);