From 99f2c9273db290b0c4bdf54651baac434f9add30 Mon Sep 17 00:00:00 2001 From: Ryan Day Date: Thu, 30 Apr 2020 21:00:13 -0400 Subject: [PATCH] Server hrr support (#1742) * Server support for Hello Retry Requests * Rewrite transcript after the HelloRetryRequest is sent. This prevents the need for cookies to store the ClientHello1 hash. * Refactor transcript rewrites into a new unit * Refactor get_hash_state. * Add an integration test for an unsupported curve that forces a retry. * OpenSSL and BoringSSL handshakes are successful * Refactor the way HelloRetryRequests are handled. * Add new handshake type to handle to multiple CH and SH messages. * SAW Tests * Update Cryptol models for the new handshake types. * Update RFC definitions to handle HelloRetryRequests. * Updates to code and tests after removing client support for HRR. * Modifications to handshake types supporting HelloRetryRequests. Small modifications based on PR feedback. * Update handshake_io_lowlevel.saw --- .travis/s2n_setup_env.sh | 3 + crypto/s2n_hash.c | 6 +- docs/images/tls12_state_machine.svg | 298 +++++++------- docs/images/tls13_state_machine.svg | 140 ------- tests/integration/common/s2n_test_scenario.py | 2 +- .../integration/s2n_tls13_handshake_tests.py | 27 +- tests/saw/spec/handshake/handshake.saw | 16 +- .../spec/handshake/handshake_io_lowlevel.saw | 12 +- tests/saw/spec/handshake/s2n_handshake_io.cry | 12 +- tests/testlib/s2n_connection_test_utils.c | 9 + tests/testlib/s2n_testlib.h | 2 + tests/unit/s2n_cipher_suite_match_test.c | 16 + ..._extensions_server_key_share_select_test.c | 20 +- tests/unit/s2n_hash_test.c | 22 +- tests/unit/s2n_server_hello_retry_test.c | 385 +++++++++++++++++- tests/unit/s2n_server_hello_test.c | 13 +- .../s2n_server_key_share_extension_test.c | 8 + .../s2n_tls13_state_machine_handshake_test.c | 11 +- tests/viz/s2n_state_machine_viz.c | 2 +- tls/extensions/s2n_client_key_share.c | 12 + tls/extensions/s2n_server_key_share.c | 71 +++- tls/s2n_client_hello.c | 30 +- tls/s2n_establish_session.c | 4 - tls/s2n_handshake.c | 67 ++- tls/s2n_handshake.h | 25 ++ tls/s2n_handshake_io.c | 174 +++----- tls/s2n_handshake_transcript.c | 176 ++++++++ tls/s2n_server_extensions.c | 8 + tls/s2n_server_hello.c | 85 ++-- tls/s2n_server_hello_retry.c | 43 +- tls/s2n_tls.h | 6 +- tls/s2n_tls13.h | 8 + tls/s2n_tls13_handshake.h | 1 + utils/s2n_blob.c | 12 +- 34 files changed, 1172 insertions(+), 554 deletions(-) create mode 100644 tls/s2n_handshake_transcript.c diff --git a/.travis/s2n_setup_env.sh b/.travis/s2n_setup_env.sh index fe84559653c..e4f465e3832 100755 --- a/.travis/s2n_setup_env.sh +++ b/.travis/s2n_setup_env.sh @@ -84,6 +84,9 @@ export S2N_CORKED_IO export S2N_NO_PQ_ASM +# Unset the FIPS flag incase the tester has changed modes after a FIPS test +unset S2N_TEST_IN_FIPS_MODE + # Select the libcrypto to build s2n against. If this is unset, default to the latest stable version(Openssl 1.1.1) if [[ -z $S2N_LIBCRYPTO ]]; then export LIBCRYPTO_ROOT=$OPENSSL_1_1_1_INSTALL_DIR ; fi if [[ "$S2N_LIBCRYPTO" == "openssl-1.1.1" ]]; then export LIBCRYPTO_ROOT=$OPENSSL_1_1_1_INSTALL_DIR ; fi diff --git a/crypto/s2n_hash.c b/crypto/s2n_hash.c index 1e38cb21363..e9a8b4952ae 100644 --- a/crypto/s2n_hash.c +++ b/crypto/s2n_hash.c @@ -199,7 +199,7 @@ static int s2n_low_level_hash_digest(struct s2n_hash_state *state, void *out, ui break; case S2N_HASH_MD5: eq_check(size, MD5_DIGEST_LENGTH); - GUARD_OSSL(MD5_Final(out, &state->digest.low_level.md5), S2N_ERR_HASH_DIGEST_FAILED); + GUARD_OSSL(MD5_Final(out, &state->digest.low_level.md5), S2N_ERR_HASH_DIGEST_FAILED); break; case S2N_HASH_SHA1: eq_check(size, SHA_DIGEST_LENGTH); @@ -406,8 +406,8 @@ static int s2n_evp_hash_copy(struct s2n_hash_state *to, struct s2n_hash_state *f if (s2n_digest_is_md5_allowed_for_fips(&from->digest.high_level.evp)) { GUARD(s2n_hash_allow_md5_for_fips(to)); } - GUARD_OSSL(EVP_MD_CTX_copy_ex(to->digest.high_level.evp.ctx, from->digest.high_level.evp.ctx), S2N_ERR_HASH_COPY_FAILED); - GUARD_OSSL(EVP_MD_CTX_copy_ex(to->digest.high_level.evp_md5_secondary.ctx, from->digest.high_level.evp_md5_secondary.ctx), S2N_ERR_HASH_COPY_FAILED); + GUARD_OSSL(EVP_MD_CTX_copy_ex(to->digest.high_level.evp.ctx, from->digest.high_level.evp.ctx), S2N_ERR_HASH_COPY_FAILED); + GUARD_OSSL(EVP_MD_CTX_copy_ex(to->digest.high_level.evp_md5_secondary.ctx, from->digest.high_level.evp_md5_secondary.ctx), S2N_ERR_HASH_COPY_FAILED); break; default: S2N_ERROR(S2N_ERR_HASH_INVALID_ALGORITHM); diff --git a/docs/images/tls12_state_machine.svg b/docs/images/tls12_state_machine.svg index 04f755253e4..7d50724fd5b 100644 --- a/docs/images/tls12_state_machine.svg +++ b/docs/images/tls12_state_machine.svg @@ -1,302 +1,290 @@ - - - + + G - -s2n TLS 1.2 State Machine + +s2n TLS 1.2 State Machine INITIAL - -INITIAL + +INITIAL CLIENT_HELLO - -CLIENT_HELLO + +CLIENT_HELLO INITIAL->CLIENT_HELLO - - - - - -SERVER_SESSION_LOOKUP - -SERVER_SESSION_LOOKUP - - - -CLIENT_HELLO->SERVER_SESSION_LOOKUP - - + + - + SERVER_HELLO - -SERVER_HELLO + +SERVER_HELLO - - -SERVER_SESSION_LOOKUP->SERVER_HELLO - - + + +CLIENT_HELLO->SERVER_HELLO + + - + SERVER_CERT - -SERVER_CERT + +SERVER_CERT - + SERVER_HELLO->SERVER_CERT - - + + - + SERVER_NEW_SESSION_TICKET - -SERVER_NEW_SESSION_TICKET + +SERVER_NEW_SESSION_TICKET - + SERVER_HELLO->SERVER_NEW_SESSION_TICKET - - + + - + SERVER_CHANGE_CIPHER_SPEC - -SERVER_CHANGE_CIPHER_SPEC + +SERVER_CHANGE_CIPHER_SPEC - + SERVER_HELLO->SERVER_CHANGE_CIPHER_SPEC - - + + - + SERVER_CERT_STATUS - -SERVER_CERT_STATUS + +SERVER_CERT_STATUS - + SERVER_CERT->SERVER_CERT_STATUS - - + + - + SERVER_KEY - -SERVER_KEY + +SERVER_KEY - + SERVER_CERT->SERVER_KEY - - + + - + SERVER_CERT_REQ - -SERVER_CERT_REQ + +SERVER_CERT_REQ - + SERVER_CERT->SERVER_CERT_REQ - - + + - + SERVER_HELLO_DONE - -SERVER_HELLO_DONE + +SERVER_HELLO_DONE - + SERVER_CERT->SERVER_HELLO_DONE - - + + - + SERVER_NEW_SESSION_TICKET->SERVER_CHANGE_CIPHER_SPEC - - + + - + SERVER_FINISHED - -SERVER_FINISHED + +SERVER_FINISHED - + SERVER_CHANGE_CIPHER_SPEC->SERVER_FINISHED - - + + - + SERVER_CERT_STATUS->SERVER_KEY - - + + - + SERVER_CERT_STATUS->SERVER_CERT_REQ - - + + - + SERVER_CERT_STATUS->SERVER_HELLO_DONE - - + + - + SERVER_KEY->SERVER_CERT_REQ - - + + - + SERVER_KEY->SERVER_HELLO_DONE - - + + - + SERVER_CERT_REQ->SERVER_HELLO_DONE - - + + - + CLIENT_CERT - -CLIENT_CERT + +CLIENT_CERT - + SERVER_HELLO_DONE->CLIENT_CERT - - + + - + CLIENT_KEY - -CLIENT_KEY + +CLIENT_KEY - + SERVER_HELLO_DONE->CLIENT_KEY - - + + - + CLIENT_CERT->CLIENT_KEY - - + + - + CLIENT_CERT_VERIFY - -CLIENT_CERT_VERIFY + +CLIENT_CERT_VERIFY - + CLIENT_KEY->CLIENT_CERT_VERIFY - - + + - + CLIENT_CHANGE_CIPHER_SPEC - -CLIENT_CHANGE_CIPHER_SPEC + +CLIENT_CHANGE_CIPHER_SPEC - + CLIENT_KEY->CLIENT_CHANGE_CIPHER_SPEC - - + + - + CLIENT_CERT_VERIFY->CLIENT_CHANGE_CIPHER_SPEC - - + + - + CLIENT_FINISHED - -CLIENT_FINISHED + +CLIENT_FINISHED - + CLIENT_CHANGE_CIPHER_SPEC->CLIENT_FINISHED - - + + - + CLIENT_FINISHED->SERVER_NEW_SESSION_TICKET - - + + - + CLIENT_FINISHED->SERVER_CHANGE_CIPHER_SPEC - - + + - + APPLICATION_DATA - -APPLICATION_DATA + +APPLICATION_DATA - + CLIENT_FINISHED->APPLICATION_DATA - - + + - + SERVER_FINISHED->CLIENT_CHANGE_CIPHER_SPEC - - + + - + SERVER_FINISHED->APPLICATION_DATA - - + + diff --git a/docs/images/tls13_state_machine.svg b/docs/images/tls13_state_machine.svg index 8521192572a..e69de29bb2d 100644 --- a/docs/images/tls13_state_machine.svg +++ b/docs/images/tls13_state_machine.svg @@ -1,140 +0,0 @@ - - - - - - -G - -s2n TLS 1.3 State Machine - - -INITIAL - -INITIAL - - - -CLIENT_HELLO - -CLIENT_HELLO - - - -INITIAL->CLIENT_HELLO - - - - - -SERVER_HELLO - -SERVER_HELLO - - - -CLIENT_HELLO->SERVER_HELLO - - - - - -SERVER_CHANGE_CIPHER_SPEC - -SERVER_CHANGE_CIPHER_SPEC - - - -SERVER_HELLO->SERVER_CHANGE_CIPHER_SPEC - - - - - -ENCRYPTED_EXTENSIONS - -ENCRYPTED_EXTENSIONS - - - -SERVER_CHANGE_CIPHER_SPEC->ENCRYPTED_EXTENSIONS - - - - - -SERVER_CERT - -SERVER_CERT - - - -SERVER_CERT_VERIFY - -SERVER_CERT_VERIFY - - - -SERVER_CERT->SERVER_CERT_VERIFY - - - - - -SERVER_FINISHED - -SERVER_FINISHED - - - -SERVER_CERT_VERIFY->SERVER_FINISHED - - - - - -CLIENT_CHANGE_CIPHER_SPEC - -CLIENT_CHANGE_CIPHER_SPEC - - - -CLIENT_FINISHED - -CLIENT_FINISHED - - - -CLIENT_CHANGE_CIPHER_SPEC->CLIENT_FINISHED - - - - - -APPLICATION_DATA - -APPLICATION_DATA - - - -CLIENT_FINISHED->APPLICATION_DATA - - - - - -ENCRYPTED_EXTENSIONS->SERVER_CERT - - - - - -SERVER_FINISHED->CLIENT_CHANGE_CIPHER_SPEC - - - - - diff --git a/tests/integration/common/s2n_test_scenario.py b/tests/integration/common/s2n_test_scenario.py index e674c3c4831..6c2529c1773 100644 --- a/tests/integration/common/s2n_test_scenario.py +++ b/tests/integration/common/s2n_test_scenario.py @@ -25,6 +25,7 @@ from enum import Enum as BaseEnum from multiprocessing.pool import ThreadPool + class Enum(BaseEnum): def __str__(self): @@ -281,4 +282,3 @@ def get_scenarios(host, start_port, s2n_modes=Mode.all(), versions=[None], ciphe port += 1 return scenarios - diff --git a/tests/integration/s2n_tls13_handshake_tests.py b/tests/integration/s2n_tls13_handshake_tests.py index a3406ea9b52..7d2db1140e0 100644 --- a/tests/integration/s2n_tls13_handshake_tests.py +++ b/tests/integration/s2n_tls13_handshake_tests.py @@ -23,7 +23,29 @@ import sys from common.s2n_test_openssl import run_openssl_connection_test -from common.s2n_test_scenario import get_scenarios, Mode, Cipher, Version +from common.s2n_test_scenario import get_scenarios, Mode, Cipher, Version, Curve +from common.s2n_test_reporting import Result, Status +import common.s2n_test_common as util + + +def verify_hrr_random_data(server, client): + """ + This callback verifies a HelloRetryRequest was sent from the S2N + server. If the rest of the integration test passes as well, then + the handshake completed after the HelloRetryRequest was sent. + """ + result = Result() + result.status = Status.FAILED + + # Start of HRR random data which will be printed in the + # client process output + marker = b"cf 21 ad 74 e5 9a 61 11 be 1d" + for line in client.stdout: + if marker in line: + result.status = Status.PASSED + break + + return result def main(): @@ -39,6 +61,9 @@ def main(): print("\n\tRunning TLS1.3 handshake tests with openssl: %s" % os.popen('openssl version').read()) failed += run_openssl_connection_test(get_scenarios(host, port, versions=[Version.TLS13], s2n_modes=Mode.all(), ciphers=Cipher.all())) + print("\n\tRunning TLS1.3 HRR tests with openssl: %s" % os.popen('openssl version').read()) + failed += run_openssl_connection_test(get_scenarios(host, port, versions=[Version.TLS13], s2n_modes=[Mode.server], ciphers=Cipher.all(), + peer_flags=['-msg', '-curves', 'X448:P-256']), test_func=verify_hrr_random_data) return failed diff --git a/tests/saw/spec/handshake/handshake.saw b/tests/saw/spec/handshake/handshake.saw index 704d4584116..63eb53fa27f 100644 --- a/tests/saw/spec/handshake/handshake.saw +++ b/tests/saw/spec/handshake/handshake.saw @@ -57,7 +57,7 @@ let prove_handshake_io_lowlevel = do { print "s2n_connection_is_managed_corked"; s2n_connection_is_managed_corked <- crucible_llvm_unsafe_assume_spec llvm "s2n_connection_is_managed_corked" s2n_connection_is_managed_corked_spec; print "s2n_generate_new_client_session_id"; - s2n_generate_new_client_session_id <- crucible_llvm_unsafe_assume_spec llvm "s2n_generate_new_client_session_id" s2n_generate_new_client_session_id_spec; + s2n_generate_new_client_session_id <- crucible_llvm_unsafe_assume_spec llvm "s2n_generate_new_client_session_id" s2n_generate_new_client_session_id_spec; print "s2n_allowed_to_cache_connection"; s2n_allowed_to_cache_connection <- crucible_llvm_unsafe_assume_spec llvm "s2n_allowed_to_cache_connection" s2n_allowed_to_cache_connection_spec; print "s2n_decrypt_session_ticket"; @@ -70,9 +70,9 @@ let prove_handshake_io_lowlevel = do { s2n_advance_message_proof <- crucible_llvm_verify llvm "s2n_advance_message" dependencies false s2n_advance_message_spec yices; print "Proving correctness of s2n_conn_set_handshake_type"; s2n_conn_set_handshake_type_proof <- crucible_llvm_verify llvm "s2n_conn_set_handshake_type" [s2n_allowed_to_cache_connection, auth_type_proof, s2n_generate_new_client_session_id, s2n_decrypt_session_ticket] false s2n_conn_set_handshake_type_spec yices; - + print "Done: Verified that the low-level specification corresponds to the C code"; - + return (); }; @@ -83,19 +83,19 @@ let prove_state_machine = do { // TLS1.3 is temporarily removed from SAW testing. // print "Checking proof that the TLS1.3 RFC simulates our Cryptol s2n spec"; // prove_print z3 {{ tls13rfcSimulatesS2N `{16} }}; - + return (); }; let prove_cork_uncork = do { print "Verifying the low-level->high-level cork-uncork simulation"; - prove_print abc {{ highLevelSimulatesLowLevel `{16} }}; - + prove_print abc {{ highLevelSimulatesLowLevel `{16} }}; + print "Verifying that double uncorking or corking cannot occur in server mode"; prove_print abc {{ noDoubleCorkUncork `{16} }}; - + print "Expecting failure when proving low-high simulation without the server mode assumption"; sat abc {{ ~highLevelDoesNotSimulateLowLevel `{16} }}; - + return (); }; diff --git a/tests/saw/spec/handshake/handshake_io_lowlevel.saw b/tests/saw/spec/handshake/handshake_io_lowlevel.saw index d2c45856a35..40b52b39a9f 100644 --- a/tests/saw/spec/handshake/handshake_io_lowlevel.saw +++ b/tests/saw/spec/handshake/handshake_io_lowlevel.saw @@ -28,7 +28,12 @@ import "s2n_handshake_io.cry"; llvm <- llvm_load_module "../../bitcode/all_llvm.bc"; print "Loaded bitcode via Crucible"; +//////////////////////////////////////////////////////////////// // References to various components of the connection state: +// The Cryptol definitions are located in the s2n_handshake_io.cry file. +// Those Cryptol definitions will be compared against the actual C +// definitions by this SAW script. +//////////////////////////////////////////////////////////////// //conn->corked_io let conn_corked_io pconn = crucible_field pconn "corked_io"; @@ -169,6 +174,8 @@ let setup_connection = do { let client_cert_auth_type = {{ if cca_ov != 0 then cca else config_cca }}; + // This returns a connection containing a handshake. If the connection or the handshake + // definitions are changed, they must also be changed here. return (pconn, {{ {corked_io = corked_io ,mode = mode ,handshake = {message_number = message_number @@ -182,8 +189,8 @@ let setup_connection = do { ,resume_from_cache = False ,client_auth_flag = if mode == S2N_CLIENT then client_cert_auth_type == 1 else if mode == S2N_SERVER then client_cert_auth_type != 0 else False - ,actual_protocol_version = version ,no_client_cert = no_client_cert != zero + ,actual_protocol_version = version } }}); }; @@ -246,6 +253,8 @@ let s2n_conn_set_handshake_type_spec = do { // we assume that the handshake struct denotes a valid handshake state // (e.g. it will not index out of bounds in the state transition array // "handshakes") + // conn.handshake is defined in s2n_handshake_io.cry. + // valid_handshake is defined in s2n_handshake_io.cry as well. crucible_precond {{ valid_handshake conn.handshake }}; // symbolically execute s2n_conn_set_handshake_type @@ -295,6 +304,7 @@ let s2n_advance_message_spec = do { // make sure the low-level spec representation of the declarative // handshake/cork-uncork state machine is equivalent to the one in // s2n + // The Cryptol representations are defined here: tests/saw/spec/handshake/s2n_handshake_io.cry crucible_points_to (crucible_global "handshakes") (crucible_term {{ handshakes }}); // TLS1.3 is temporarily removed from SAW testing. diff --git a/tests/saw/spec/handshake/s2n_handshake_io.cry b/tests/saw/spec/handshake/s2n_handshake_io.cry index 58d931718b4..c95bb652bdc 100644 --- a/tests/saw/spec/handshake/s2n_handshake_io.cry +++ b/tests/saw/spec/handshake/s2n_handshake_io.cry @@ -170,7 +170,7 @@ IS_TLS13_HANDSHAKE conn = conn.actual_protocol_version == S2N_TLS13 ACTIVE_STATE_MACHINE : connection -> [19]handshake_action ACTIVE_STATE_MACHINE conn = if IS_TLS13_HANDSHAKE conn then tls13_state_machine else state_machine # zero -ACTIVE_HANDSHAKES : connection -> [128][32][32] +ACTIVE_HANDSHAKES : connection -> [256][32][32] ACTIVE_HANDSHAKES conn = if IS_TLS13_HANDSHAKE conn then tls13_handshakes else handshakes ACTIVE_MESSAGE : connection -> [32] @@ -257,8 +257,8 @@ state_machine_fn m = else zero // A model of the tls1.3 handshake state machine (array handshakes in C) -tls13_handshakes : [128][32][32] -tls13_handshakes = [tls13_handshakes_fn h | h <- [0..127]] +tls13_handshakes : [256][32][32] +tls13_handshakes = [tls13_handshakes_fn h | h <- [0..255]] // A function that gives the tls1.3 handshake sequence for each valid handshake_type. tls13_handshakes_fn: [32] -> [32][32] @@ -285,8 +285,8 @@ tls13_handshakes_fn handshk = else zero // A model of the handshake state machine (array handshakes in C) -handshakes : [128][32][32] -handshakes = [handshakes_fn h | h <- [0..127]] +handshakes : [256][32][32] +handshakes = [handshakes_fn h | h <- [0..255]] // A function that gives the handshake sequence for each valid handshake_type. // This is a sparse encoding of the handshakes array (which is sparse too). @@ -512,7 +512,7 @@ SERVER_CHANGE_CIPHER_SPEC = 13 SERVER_FINISHED = 14 ENCRYPTED_EXTENSIONS = 15 SERVER_CERT_VERIFY = 16 -APPLICATION_DATA = 17 +APPLICATION_DATA = 18 //TLS record type TLS_CHANGE_CIPHER_SPEC = 20 diff --git a/tests/testlib/s2n_connection_test_utils.c b/tests/testlib/s2n_connection_test_utils.c index 754abd5d2c7..589f36eed93 100644 --- a/tests/testlib/s2n_connection_test_utils.c +++ b/tests/testlib/s2n_connection_test_utils.c @@ -201,3 +201,12 @@ void s2n_print_connection(struct s2n_connection *conn, const char *marker) } printf("\n"); } + +int s2n_set_connection_hello_retry_flags(struct s2n_connection *conn) +{ + conn->actual_protocol_version = S2N_TLS13; + conn->handshake.message_number = 1; + conn->handshake.handshake_type = NEGOTIATED | HELLO_RETRY_REQUEST | FULL_HANDSHAKE; + + return 0; +} diff --git a/tests/testlib/s2n_testlib.h b/tests/testlib/s2n_testlib.h index be93da133a0..a7bdb7ee094 100644 --- a/tests/testlib/s2n_testlib.h +++ b/tests/testlib/s2n_testlib.h @@ -55,6 +55,8 @@ int s2n_connections_set_piped_io(struct s2n_connection *client, struct s2n_conne int s2n_fd_set_blocking(int fd); int s2n_fd_set_non_blocking(int fd); +int s2n_set_connection_hello_retry_flags(struct s2n_connection *conn); + #define S2N_MAX_TEST_PEM_SIZE 4096 /* These paths assume that the unit tests are run from inside the unit/ directory. diff --git a/tests/unit/s2n_cipher_suite_match_test.c b/tests/unit/s2n_cipher_suite_match_test.c index 704a9f3a52f..1ba97edc1b4 100644 --- a/tests/unit/s2n_cipher_suite_match_test.c +++ b/tests/unit/s2n_cipher_suite_match_test.c @@ -572,6 +572,22 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_connection_wipe(conn)); } + /* Client sends cipher which is not in the configured suite */ + { + EXPECT_SUCCESS(s2n_enable_tls13()); + uint8_t invalid_cipher_pref[] = { + TLS_ECDHE_BIKE_RSA_WITH_AES_256_GCM_SHA384 + }; + + const uint8_t invalid_cipher_count = sizeof(invalid_cipher_pref) / S2N_TLS_CIPHER_SUITE_LEN; + s2n_connection_set_cipher_preferences(conn, "default_tls13"); + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, invalid_cipher_pref, invalid_cipher_count), S2N_ERR_CIPHER_NOT_SUPPORTED); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_disable_tls13()); + } + EXPECT_SUCCESS(s2n_config_free(server_config)); EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert)); EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); diff --git a/tests/unit/s2n_extensions_server_key_share_select_test.c b/tests/unit/s2n_extensions_server_key_share_select_test.c index 658bffc43e7..2805707b8ba 100644 --- a/tests/unit/s2n_extensions_server_key_share_select_test.c +++ b/tests/unit/s2n_extensions_server_key_share_select_test.c @@ -48,8 +48,7 @@ int main(int argc, char **argv) EXPECT_FAILURE_WITH_ERRNO(s2n_extensions_server_key_share_select(server_conn), S2N_ERR_ECDHE_UNSUPPORTED_CURVE); EXPECT_NULL(server_conn->secure.server_ecc_evp_params.negotiated_curve); - /* Commented out until hello retry is implemented in issue #1607. - EXPECT_FALSE(s2n_server_requires_retry(server_conn)); */ + EXPECT_FALSE(s2n_is_hello_retry_required(server_conn)); EXPECT_SUCCESS(s2n_connection_free(server_conn)); } @@ -69,8 +68,7 @@ int main(int argc, char **argv) EXPECT_FAILURE_WITH_ERRNO(s2n_extensions_server_key_share_select(server_conn), S2N_ERR_ECDHE_UNSUPPORTED_CURVE); EXPECT_NULL(server_conn->secure.server_ecc_evp_params.negotiated_curve); - /* Commented out until hello retry is implemented in issue #1607. - EXPECT_FALSE(s2n_server_requires_retry(server_conn)); */ + EXPECT_FALSE(s2n_is_hello_retry_required(server_conn)); EXPECT_SUCCESS(s2n_connection_free(server_conn)); } @@ -88,13 +86,11 @@ int main(int argc, char **argv) EXPECT_NULL(server_conn->secure.client_ecc_evp_params[i].evp_pkey); EXPECT_NULL(server_conn->secure.client_ecc_evp_params[i].negotiated_curve); } - /* Commented out until hello retry request is implemented in issue #1607. - EXPECT_SUCCESS(s2n_extensions_server_key_share_select(server_conn)); */ - EXPECT_FAILURE_WITH_ERRNO(s2n_extensions_server_key_share_select(server_conn), S2N_ERR_BAD_KEY_SHARE); + EXPECT_SUCCESS(s2n_extensions_server_key_share_select(server_conn)); EXPECT_EQUAL(server_conn->secure.server_ecc_evp_params.negotiated_curve, ecc_pref->ecc_curves[0]); - /* Commented out until hello retry is implemented in issue #1607. - EXPECT_TRUE(s2n_server_requires_retry(server_conn)); */ + + EXPECT_EQUAL(s2n_is_hello_retry_required(server_conn), 1); EXPECT_SUCCESS(s2n_connection_free(server_conn)); } @@ -118,8 +114,7 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_extensions_server_key_share_select(server_conn)); EXPECT_EQUAL(server_conn->secure.server_ecc_evp_params.negotiated_curve, ecc_pref->ecc_curves[1]); - /* Commented out until hello retry is implemented in issue #1607. - EXPECT_FALSE(s2n_server_requires_retry(server_conn)); */ + EXPECT_FALSE(s2n_is_hello_retry_required(server_conn)); EXPECT_SUCCESS(s2n_connection_free(server_conn)); } @@ -139,8 +134,7 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_extensions_server_key_share_select(server_conn)); EXPECT_EQUAL(server_conn->secure.server_ecc_evp_params.negotiated_curve, ecc_pref->ecc_curves[0]); - /* Commented out until hello retry is implemented in issue #1607. - EXPECT_FALSE(s2n_server_requires_retry(server_conn)); */ + EXPECT_FALSE(s2n_is_hello_retry_required(server_conn)); EXPECT_SUCCESS(s2n_connection_free(server_conn)); } diff --git a/tests/unit/s2n_hash_test.c b/tests/unit/s2n_hash_test.c index c37fa613450..81d2cec57e8 100644 --- a/tests/unit/s2n_hash_test.c +++ b/tests/unit/s2n_hash_test.c @@ -51,18 +51,18 @@ int main(int argc, char **argv) GUARD(s2n_hash_digest_size(S2N_HASH_MD5, &md5_digest_size)); EXPECT_EQUAL(md5_digest_size, 16); EXPECT_SUCCESS(s2n_hash_init(&hash, S2N_HASH_MD5)); - EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); - EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); - EXPECT_EQUAL(bytes_in_hash, 0); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 0); EXPECT_SUCCESS(s2n_hash_update(&hash, hello, strlen((char *)hello))); - EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); - EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); - EXPECT_EQUAL(bytes_in_hash, 13); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 13); EXPECT_SUCCESS(s2n_hash_digest(&hash, digest_pad, MD5_DIGEST_LENGTH)); - EXPECT_FALSE(s2n_hash_is_ready_for_input(&hash)); - EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); for (int i = 0; i < 16; i++) { @@ -73,9 +73,9 @@ int main(int argc, char **argv) EXPECT_EQUAL(memcmp(output_pad, "59ca0efa9f5633cb0371bbc0355478d8", 16 * 2), 0); GUARD(s2n_hash_reset(&hash)); - EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); - EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); - EXPECT_EQUAL(bytes_in_hash, 0); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 0); } /* Try SHA1 */ diff --git a/tests/unit/s2n_server_hello_retry_test.c b/tests/unit/s2n_server_hello_retry_test.c index a1715a92ac1..5f299f0722a 100644 --- a/tests/unit/s2n_server_hello_retry_test.c +++ b/tests/unit/s2n_server_hello_retry_test.c @@ -15,15 +15,396 @@ #include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#include "tls/extensions/s2n_key_share.h" +#include "tls/extensions/s2n_server_supported_versions.h" + +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_ecc_preferences.h" #include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" + +#include "tls/extensions/s2n_server_key_share.h" + #include "error/s2n_errno.h" +const uint8_t SESSION_ID_SIZE = 1; +const uint8_t COMPRESSION_METHOD_SIZE = 1; + +/* from RFC: https://tools.ietf.org/html/rfc8446#section-4.1.3*/ +const uint8_t hello_retry_request_random_test_buffer[S2N_TLS_RANDOM_DATA_LEN] = { + 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, + 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C +}; + +struct client_hello_context { + int invocations; +}; + +static int client_hello_detect_duplicate_calls(struct s2n_connection *conn, void *ctx) +{ + if (ctx == NULL) { + return -1; + } + + struct client_hello_context *client_hello_ctx = ctx; + + /* Incremet counter */ + client_hello_ctx->invocations++; + + return 0; +} + + int main(int argc, char **argv) { BEGIN_TEST(); - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_send(NULL), S2N_ERR_UNIMPLEMENTED); - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(NULL), S2N_ERR_UNIMPLEMENTED); + EXPECT_SUCCESS(s2n_enable_tls13()); + + /* Send and receive Hello Retry Request messages */ + { + struct s2n_config *server_config; + struct s2n_config *client_config; + + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_stuffer *server_stuffer = &server_conn->handshake.io; + + uint32_t total = S2N_TLS_PROTOCOL_VERSION_LEN + + S2N_TLS_RANDOM_DATA_LEN + + SESSION_ID_SIZE + + server_conn->session_id_len + + S2N_TLS_CIPHER_SUITE_LEN + + COMPRESSION_METHOD_SIZE; + + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure.cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + server_conn->secure.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + server_conn->secure.client_ecc_evp_params[0].negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_conn->secure.client_ecc_evp_params[0])); + + s2n_set_connection_hello_retry_flags(server_conn); + + /* The client will need a key share extension to properly parse the hello */ + /* Total extension size + size of each extension */ + total += 2 + s2n_extensions_server_supported_versions_size(server_conn) + + s2n_extensions_server_key_share_send_size(server_conn); + + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + EXPECT_EQUAL(s2n_is_hello_retry_required(server_conn), 1); + + EXPECT_EQUAL(s2n_stuffer_data_available(server_stuffer), total); + + /* Copy server stuffer to client stuffer */ + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, total)); + + /* Test that s2n_server_hello_retry_recv() is unsupported */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(client_conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + + /* Verify the requires_retry flag causes a retry to be sent */ + { + struct s2n_config *conf; + struct s2n_connection *conn; + + EXPECT_NOT_NULL(conf = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); + + conn->client_protocol_version = S2N_TLS13; + conn->server_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + + conn->secure.cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + conn->secure.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + conn->secure.client_ecc_evp_params[0].negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->secure.client_ecc_evp_params[0])); + + s2n_set_connection_hello_retry_flags(conn); + + EXPECT_SUCCESS(s2n_server_hello_retry_send(conn)); + + EXPECT_EQUAL(s2n_is_hello_retry_required(conn), 1); + + EXPECT_SUCCESS(s2n_config_free(conf)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Retry requests with incorrect random data are not accepted */ + { + struct s2n_config *conf; + struct s2n_connection *conn; + + EXPECT_NOT_NULL(conf = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); + + struct s2n_stuffer *io = &conn->handshake.io; + conn->server_protocol_version = S2N_TLS13; + + /* protocol version */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 / 10)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 % 10)); + + /* random data */ + uint8_t bad_retry_random[S2N_TLS_RANDOM_DATA_LEN] = {0}; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, bad_retry_random, S2N_TLS_RANDOM_DATA_LEN)); + + /* session id */ + uint8_t session_id[S2N_TLS_SESSION_ID_MAX_LEN] = {0}; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS_SESSION_ID_MAX_LEN)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, session_id, S2N_TLS_SESSION_ID_MAX_LEN)); + + /* cipher suites */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(io, 0x1301)); + + /* no compression */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 0)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(s2n_is_hello_retry_required(conn), 0); + + EXPECT_SUCCESS(s2n_config_free(conf)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Retry requests without a supported version extension are not accepted */ + { + struct s2n_config *conf; + struct s2n_connection *conn; + + EXPECT_NOT_NULL(conf = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); + + struct s2n_stuffer *io = &conn->handshake.io; + conn->server_protocol_version = S2N_TLS13; + + /* protocol version */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 / 10)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 % 10)); + + /* random data */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, hello_retry_request_random_test_buffer, S2N_TLS_RANDOM_DATA_LEN)); + + /* session id */ + uint8_t session_id[S2N_TLS_SESSION_ID_MAX_LEN] = {0}; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS_SESSION_ID_MAX_LEN)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, session_id, S2N_TLS_SESSION_ID_MAX_LEN)); + + /* cipher suites */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(io, (0x13 << 8) + 0x01)); + + /* no compression */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 0)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(s2n_is_hello_retry_required(conn), 0); + + EXPECT_SUCCESS(s2n_config_free(conf)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Verify the client key share extension properly handles HelloRetryRequests */ + { + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer* extension_stuffer = &server_conn->handshake.io; + + server_conn->secure.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + server_conn->secure.client_ecc_evp_params[0].negotiated_curve = s2n_all_supported_curves_list[0]; + s2n_set_connection_hello_retry_flags(server_conn); + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_conn->secure.client_ecc_evp_params[0])); + EXPECT_SUCCESS(s2n_extensions_server_key_share_send(server_conn, extension_stuffer)); + + S2N_STUFFER_READ_EXPECT_EQUAL(extension_stuffer, TLS_EXTENSION_KEY_SHARE, uint16); + /* 4 = S2N_SIZE_OF_EXTENSION_TYPE + S2N_SIZE_OF_EXTENSION_DATA_SIZE */ + S2N_STUFFER_READ_EXPECT_EQUAL(extension_stuffer, s2n_extensions_server_key_share_send_size(server_conn) - 4, uint16); + + client_conn->secure.client_ecc_evp_params[0].negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->secure.client_ecc_evp_params[0])); + + /* Setup the client to receive a HelloRetryRequest */ + memcpy_check(client_conn->secure.server_random, hello_retry_request_random_test_buffer, S2N_TLS_RANDOM_DATA_LEN); + client_conn->server_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_set_hello_retry_required(client_conn)); + + /* Parse the key share */ + EXPECT_SUCCESS(s2n_extensions_server_key_share_recv(client_conn, extension_stuffer)); + EXPECT_EQUAL(s2n_stuffer_data_available(extension_stuffer), 0); + + /* This curve will be NULL even though the extension succeeded */ + EXPECT_NULL(client_conn->secure.server_ecc_evp_params.negotiated_curve); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + } + + /* Verify that the hash transcript recreation function correctly takes the existing ClientHello1 + * hash, and generates a synthetic message. */ + { + struct s2n_config *conf; + struct s2n_connection *conn; + + EXPECT_NOT_NULL(conf = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); + + conn->server_protocol_version = S2N_TLS13; + conn->secure.cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + conn->secure.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + conn->secure.client_ecc_evp_params[0].negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->secure.client_ecc_evp_params[0])); + + /* This blob is taken from the functional test RFC. That RFC does not actually provide hash transcript + * values, so the expected hashes are taken from what our hash functions generated and the hash + * generated from the transcript recreation. + * https://tools.ietf.org/html/rfc8448#section-5 */ + S2N_BLOB_FROM_HEX(client_hello1, + "010000c00303cb34ecb1e78163" + "ba1c38c6dacb196a6dffa21a8d9912ec18a2ef6283" + "024dece7000006130113031302010000910000000b" + "0009000006736572766572ff01000100000a001400" + "12001d001700180019010001010102010301040023" + "0000003300260024001d002099381de560e4bd43d2" + "3d8e435a7dbafeb3c06e51c13cae4d5413691e529a" + "af2c002b0003020304000d0020001e040305030603" + "020308040805080604010501060102010402050206" + "020202002d00020101001c00024001"); + + S2N_BLOB_FROM_HEX(client_hello1_expected_hash, + "4db255f30da09a407c841720be831a06a5aa9b3662a5f44267d37706b73c2b8c"); + + S2N_BLOB_FROM_HEX(synthetic_message_with_ch1_expected_hash, + "ff1135ed878322e29699da3e451d2f08bf11fc693038769978e75bb63304a225"); + + EXPECT_SUCCESS(s2n_conn_update_handshake_hashes(conn, &client_hello1)); + + s2n_tls13_connection_keys(keys, conn); + uint8_t hash_digest_length = keys.size; + struct s2n_blob compare_blob; + + struct s2n_hash_state hash_state, client_hello1_hash; + GUARD(s2n_handshake_get_hash_state(conn, keys.hash_algorithm, &hash_state)); + + uint8_t client_hello1_digest_out[S2N_MAX_DIGEST_LEN]; + EXPECT_SUCCESS(s2n_hash_new(&client_hello1_hash)); + EXPECT_SUCCESS(s2n_hash_copy(&client_hello1_hash, &hash_state)); + EXPECT_SUCCESS(s2n_hash_digest(&client_hello1_hash, client_hello1_digest_out, hash_digest_length)); + EXPECT_SUCCESS(s2n_hash_free(&client_hello1_hash)); + + EXPECT_SUCCESS(s2n_blob_init(&compare_blob, client_hello1_digest_out, hash_digest_length)); + S2N_BLOB_EXPECT_EQUAL(client_hello1_expected_hash, compare_blob); + + EXPECT_SUCCESS(s2n_server_hello_retry_recreate_transcript(conn)); + + struct s2n_hash_state recreated_hash; + uint8_t recreated_transcript_digest_out[S2N_MAX_DIGEST_LEN]; + GUARD(s2n_handshake_get_hash_state(conn, keys.hash_algorithm, &hash_state)); + EXPECT_SUCCESS(s2n_hash_new(&recreated_hash)); + EXPECT_SUCCESS(s2n_hash_copy(&recreated_hash, &hash_state)); + EXPECT_SUCCESS(s2n_hash_digest(&recreated_hash, recreated_transcript_digest_out, hash_digest_length)); + EXPECT_SUCCESS(s2n_hash_free(&recreated_hash)); + + EXPECT_SUCCESS(s2n_blob_init(&compare_blob, recreated_transcript_digest_out, hash_digest_length)); + S2N_BLOB_EXPECT_EQUAL(synthetic_message_with_ch1_expected_hash, compare_blob); + + EXPECT_SUCCESS(s2n_config_free(conf)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Send and receive Hello Retry Request messages */ + { + struct s2n_config *server_config; + struct s2n_config *client_config; + + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + struct s2n_cert_chain_and_key *tls13_chain_and_key; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = {0}; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = {0}; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + struct client_hello_context client_hello_ctx = {.invocations = 0 }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, client_hello_detect_duplicate_calls, &client_hello_ctx)); + + /* Send the first CH message */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + + /* Receive the CH and send an HRR, which will execute the HRR code paths */ + EXPECT_EQUAL(client_hello_ctx.invocations, 0); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(client_hello_ctx.invocations, 1); + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + /* Before sending the second message, clear out the existing keys. + * Otherwise we will leak memory in this test. */ + const struct s2n_ecc_preferences *ecc_pref = client_conn->config->ecc_preferences; + for (int i=0; icount; i++) { + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_conn->secure.client_ecc_evp_params[i])); + } + + /* Send the second CH message */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + + /* Verify that receiving the second CH message does not execute the callback */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(client_hello_ctx.invocations, 1); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + } + + + EXPECT_SUCCESS(s2n_disable_tls13()); END_TEST(); } diff --git a/tests/unit/s2n_server_hello_test.c b/tests/unit/s2n_server_hello_test.c index bda0da4a87e..2644b0fecf7 100644 --- a/tests/unit/s2n_server_hello_test.c +++ b/tests/unit/s2n_server_hello_test.c @@ -244,6 +244,9 @@ int main(int argc, char **argv) + S2N_TLS_CIPHER_SUITE_LEN + COMPRESSION_METHOD_SIZE; + /* Set the negotiated curve, otherwise the server might try to respond with a retry */ + server_conn->secure.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + /* The server will respond with TLS1.1 even though it supports TLS1.3 */ server_conn->actual_protocol_version = S2N_TLS11; EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); @@ -294,6 +297,9 @@ int main(int argc, char **argv) + S2N_TLS_CIPHER_SUITE_LEN + COMPRESSION_METHOD_SIZE; + /* Set the negotiated curve, otherwise the server might try to respond with a retry */ + server_conn->secure.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + /* The server will respond with TLS1.2 even though it supports TLS1.3 */ server_conn->actual_protocol_version = S2N_TLS12; EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); @@ -481,18 +487,21 @@ int main(int argc, char **argv) uint8_t session_id[S2N_TLS_SESSION_ID_MAX_LEN] = {0}; S2N_BLOB_FROM_HEX(random_blob, hello_retry_random_hex); + /* random payload */ EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, random_blob.data, S2N_TLS_RANDOM_DATA_LEN)); + /* session id */ EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS_SESSION_ID_MAX_LEN)); EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, session_id, S2N_TLS_SESSION_ID_MAX_LEN)); + /* cipher suites */ EXPECT_SUCCESS(s2n_stuffer_write_uint16(io, (0x13 << 8) + 0x01)); + /* no compression */ EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 0)); EXPECT_EQUAL(S2N_TLS_RANDOM_DATA_LEN, random_blob.size); - /* Test s2n_server_hello_recv() fails with Unimplemented method error */ - EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_server_hello_recv(client_conn), S2N_ERR_UNIMPLEMENTED); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); } diff --git a/tests/unit/s2n_server_key_share_extension_test.c b/tests/unit/s2n_server_key_share_extension_test.c index fb614daea0a..3abf3b5b812 100644 --- a/tests/unit/s2n_server_key_share_extension_test.c +++ b/tests/unit/s2n_server_key_share_extension_test.c @@ -64,6 +64,7 @@ int main(int argc, char **argv) { struct s2n_connection *conn; EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_EQUAL(0, s2n_extensions_server_key_share_send_size(conn)); EXPECT_NOT_NULL(conn->config); const struct s2n_ecc_preferences *ecc_pref = conn->config->ecc_preferences; @@ -78,6 +79,13 @@ int main(int argc, char **argv) conn->secure.server_ecc_evp_params.negotiated_curve = NULL; EXPECT_EQUAL(0, s2n_extensions_server_key_share_send_size(conn)); + /* A HelloRetryRequest only requires a Selected Group, not a key share */ + conn->actual_protocol_version = S2N_TLS13; + conn->handshake.message_number = 1; + conn->handshake.handshake_type = NEGOTIATED | HELLO_RETRY_REQUEST | FULL_HANDSHAKE; + conn->secure.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_EQUAL(6, s2n_extensions_server_key_share_send_size(conn)); + EXPECT_SUCCESS(s2n_connection_free(conn)); } diff --git a/tests/unit/s2n_tls13_state_machine_handshake_test.c b/tests/unit/s2n_tls13_state_machine_handshake_test.c index be6998dc6c4..6afe2cd112f 100644 --- a/tests/unit/s2n_tls13_state_machine_handshake_test.c +++ b/tests/unit/s2n_tls13_state_machine_handshake_test.c @@ -305,7 +305,7 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_connection_free(conn)); } - /* Test: TLS1.3 s2n_conn_set_handshake_type sets FULL_HANDSHAKE and CLIENT_AUTH*/ + /* Test: TLS1.3 s2n_conn_set_handshake_type sets FULL_HANDSHAKE, HELLO_RETRY_REQUEST, and CLIENT_AUTH*/ { struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); @@ -335,6 +335,12 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); EXPECT_EQUAL(conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH); + /* Verify that tls1.3 DOES NOT set the flags even when a HelloRetryRequest is in the mix */ + conn->actual_protocol_version = S2N_TLS13; + conn->handshake.handshake_type = INITIAL | HELLO_RETRY_REQUEST; + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | HELLO_RETRY_REQUEST); + EXPECT_SUCCESS(s2n_connection_free(conn)); } @@ -349,6 +355,9 @@ int main(int argc, char **argv) conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE", s2n_connection_get_handshake_type_name(conn)); + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST; + EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE|HELLO_RETRY_REQUEST", s2n_connection_get_handshake_type_name(conn)); + const char* all_flags_handshake_type_name = "NEGOTIATED|FULL_HANDSHAKE|CLIENT_AUTH|WITH_SESSION_TICKET|NO_CLIENT_CERT"; conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | WITH_SESSION_TICKET | NO_CLIENT_CERT; EXPECT_STRING_EQUAL(all_flags_handshake_type_name, s2n_connection_get_handshake_type_name(conn)); diff --git a/tests/viz/s2n_state_machine_viz.c b/tests/viz/s2n_state_machine_viz.c index 43e0c6f6ca7..b7426475421 100644 --- a/tests/viz/s2n_state_machine_viz.c +++ b/tests/viz/s2n_state_machine_viz.c @@ -18,7 +18,7 @@ #include "tls/s2n_handshake_io.c" -#define MAX_STATE_TYPE (SERVER_CERT_VERIFY + 1) +#define MAX_STATE_TYPE (APPLICATION_DATA + 1) struct state { const char *name; diff --git a/tls/extensions/s2n_client_key_share.c b/tls/extensions/s2n_client_key_share.c index 8f02704ef88..af3e54d2a08 100644 --- a/tls/extensions/s2n_client_key_share.c +++ b/tls/extensions/s2n_client_key_share.c @@ -16,6 +16,7 @@ #include "tls/extensions/s2n_client_key_share.h" #include "tls/extensions/s2n_key_share.h" #include "tls/s2n_ecc_preferences.h" +#include "tls/s2n_tls13.h" #include "error/s2n_errno.h" #include "stuffer/s2n_stuffer.h" @@ -64,6 +65,9 @@ int s2n_extensions_client_key_share_recv(struct s2n_connection *conn, struct s2n uint16_t named_group, share_size; uint32_t supported_curve_index; + /* Whether a match was found */ + uint8_t match = 0; + /* bytes_processed is declared as a uint32_t to avoid integer overflow in later calculations */ uint32_t bytes_processed = 0; @@ -108,9 +112,17 @@ int s2n_extensions_client_key_share_recv(struct s2n_connection *conn, struct s2n /* Ignore curves with points we can't parse */ conn->secure.client_ecc_evp_params[supported_curve_index].negotiated_curve = NULL; GUARD(s2n_ecc_evp_params_free(&conn->secure.client_ecc_evp_params[supported_curve_index])); + } else { + match = 1; } } + /* If there was no matching key share then we received an empty key share extension + * or we didn't match a keyshare with a supported group. We should send a retry. */ + if (match == 0) { + GUARD(s2n_set_hello_retry_required(conn)); + } + return 0; } diff --git a/tls/extensions/s2n_server_key_share.c b/tls/extensions/s2n_server_key_share.c index 36589e06730..73274353cc0 100644 --- a/tls/extensions/s2n_server_key_share.c +++ b/tls/extensions/s2n_server_key_share.c @@ -13,13 +13,14 @@ * permissions and limitations under the License. */ +#include "tls/s2n_tls.h" #include "tls/extensions/s2n_server_key_share.h" #include "tls/s2n_client_extensions.h" #include "tls/s2n_ecc_preferences.h" #include "utils/s2n_safety.h" -#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" /* * Check whether client has sent a corresponding curve and key_share @@ -30,6 +31,13 @@ int s2n_extensions_server_key_share_send_check(struct s2n_connection *conn) const struct s2n_ecc_preferences *ecc_pref = conn->config->ecc_preferences; notnull_check(ecc_pref); + /* If we are responding to a retry request then we don't have a valid + * curve from the client. Just return 0 so a selected group will be + * chosen for the key share. */ + if (s2n_is_hello_retry_required(conn)) { + return 0; + } + const struct s2n_ecc_named_curve *server_curve, *client_curve; server_curve = conn->secure.server_ecc_evp_params.negotiated_curve; notnull_check(server_curve); @@ -41,6 +49,7 @@ int s2n_extensions_server_key_share_send_check(struct s2n_connection *conn) break; } } + gt_check(curve_index, -1); const struct s2n_ecc_evp_params client_ecc_evp = conn->secure.client_ecc_evp_params[curve_index]; @@ -52,6 +61,7 @@ int s2n_extensions_server_key_share_send_check(struct s2n_connection *conn) return 0; } + /* * Selects highest priority mutually supported keyshare */ @@ -63,19 +73,18 @@ int s2n_extensions_server_key_share_select(struct s2n_connection *conn) for (uint32_t i = 0; i < ecc_pref->count; i++) { /* Checks supported group and keyshare have both been sent */ if (conn->secure.client_ecc_evp_params[i].negotiated_curve && - conn->secure.mutually_supported_groups[i]) { + conn->secure.mutually_supported_groups[i]) { conn->secure.server_ecc_evp_params.negotiated_curve = conn->secure.client_ecc_evp_params[i].negotiated_curve; return 0; } } + /* Client sent no keyshares, need to send Hello Retry Request with first negotiated curve */ if (conn->secure.server_ecc_evp_params.negotiated_curve) { - /* Once hello retry request is implemented in issue #1607, lines can - be uncommented and error can be removed. - GUARD(s2n_server_should_retry(conn)); - return 0; */ - S2N_ERROR(S2N_ERR_BAD_KEY_SHARE); - } + GUARD(s2n_set_hello_retry_required(conn)); + return 0; + } + S2N_ERROR(S2N_ERR_ECDHE_UNSUPPORTED_CURVE); } @@ -83,21 +92,29 @@ int s2n_extensions_server_key_share_select(struct s2n_connection *conn) * Calculate the data length for Server Key Share extension * based on negotiated_curve selected in server_ecc_evp_params. * + * Retry requests have a different key share format, + * https://tools.ietf.org/html/rfc8446#section-4.2.8 + * * This functions does not error, but s2n_extensions_server_key_share_send() would */ int s2n_extensions_server_key_share_send_size(struct s2n_connection *conn) { const struct s2n_ecc_named_curve* curve = conn->secure.server_ecc_evp_params.negotiated_curve; + int key_share_size = S2N_SIZE_OF_EXTENSION_TYPE + + S2N_SIZE_OF_EXTENSION_DATA_SIZE + + S2N_SIZE_OF_NAMED_GROUP; + + /* If this is a KeyShareHelloRetryRequest we don't include the share size */ + if (s2n_is_hello_retry_required(conn)) { + return key_share_size; + } if (curve == NULL) { return 0; } - const int key_share_size = S2N_SIZE_OF_EXTENSION_TYPE - + S2N_SIZE_OF_EXTENSION_DATA_SIZE - + S2N_SIZE_OF_NAMED_GROUP - + S2N_SIZE_OF_KEY_SHARE_SIZE - + curve->share_size; + /* If this is a full KeyShareEntry, include the share size */ + key_share_size += (S2N_SIZE_OF_KEY_SHARE_SIZE + curve->share_size); return key_share_size; } @@ -110,6 +127,7 @@ int s2n_extensions_server_key_share_send_size(struct s2n_connection *conn) int s2n_extensions_server_key_share_send(struct s2n_connection *conn, struct s2n_stuffer *out) { GUARD(s2n_extensions_server_key_share_send_check(conn)); + notnull_check(out); GUARD(s2n_stuffer_write_uint16(out, TLS_EXTENSION_KEY_SHARE)); @@ -118,6 +136,17 @@ int s2n_extensions_server_key_share_send(struct s2n_connection *conn, struct s2n - S2N_SIZE_OF_EXTENSION_DATA_SIZE )); + /* Retry requests only require the selected named group, not an actual share. + * https://tools.ietf.org/html/rfc8446#section-4.2.8 */ + if (s2n_is_hello_retry_required(conn)) { + notnull_check(conn->secure.server_ecc_evp_params.negotiated_curve); + + /* There was a mutually supported group, so that is the group we will select */ + uint16_t curve = conn->secure.server_ecc_evp_params.negotiated_curve->iana_id; + GUARD(s2n_stuffer_write_uint16(out, curve)); + return 0; + } + GUARD(s2n_ecdhe_parameters_send(&conn->secure.server_ecc_evp_params, out)); return 0; @@ -138,13 +167,21 @@ int s2n_extensions_server_key_share_recv(struct s2n_connection *conn, struct s2n uint16_t named_group, share_size; - /* Make sure we can read the next 4 bytes */ - S2N_ERROR_IF(s2n_stuffer_data_available(extension) < 4, S2N_ERR_BAD_KEY_SHARE); - + /* Make sure we can read the 2 byte named group */ + S2N_ERROR_IF(s2n_stuffer_data_available(extension) < 2, S2N_ERR_BAD_KEY_SHARE); GUARD(s2n_stuffer_read_uint16(extension, &named_group)); + + /* If this is a HelloRetryRequest, we won't have a key share. We just have the selected group. + * Exit early so a proper keyshare can be generated. */ + if (s2n_is_hello_retry_required(conn)) { + return 0; + } + + /* Make sure we can read the 2 byte key share size */ + S2N_ERROR_IF(s2n_stuffer_data_available(extension) < 2, S2N_ERR_BAD_KEY_SHARE); GUARD(s2n_stuffer_read_uint16(extension, &share_size)); - /* and the remaining amount of bytes */ + /* Verify that *share_size* bytes are available in the stuffer */ S2N_ERROR_IF(s2n_stuffer_data_available(extension) < share_size, S2N_ERR_BAD_KEY_SHARE); int supported_curve_index = -1; diff --git a/tls/s2n_client_hello.c b/tls/s2n_client_hello.c index d359849a369..d7117f0ba6c 100644 --- a/tls/s2n_client_hello.c +++ b/tls/s2n_client_hello.c @@ -199,6 +199,7 @@ static int s2n_parse_client_hello(struct s2n_connection *conn) client_hello->cipher_suites.size = cipher_suites_length; client_hello->cipher_suites.data = s2n_stuffer_raw_read(in, cipher_suites_length); notnull_check(client_hello->cipher_suites.data); + /* Don't choose the cipher yet, read the extensions first */ uint8_t num_compression_methods = 0; GUARD(s2n_stuffer_read_uint8(in, &num_compression_methods)); @@ -345,20 +346,25 @@ int s2n_client_hello_recv(struct s2n_connection *conn) GUARD(s2n_populate_client_hello_extensions(&conn->client_hello)); - /* Mark the collected client hello as available when parsing is done and before the client hello callback */ - conn->client_hello.parsed = 1; - - /* Call client_hello_cb if exists, letting application to modify s2n_connection or swap s2n_config */ - if (conn->config->client_hello_cb) { - int rc = conn->config->client_hello_cb(conn, conn->config->client_hello_cb_ctx); - if (rc < 0) { - GUARD(s2n_queue_reader_handshake_failure_alert(conn)); - S2N_ERROR(S2N_ERR_CANCELLED); - } - if (rc) { - conn->server_name_used = 1; + /* If the CLIENT_HELLO has already been parsed, then we should not call + * the client_hello_cb a second time. */ + if (conn->client_hello.parsed == 0) { + /* Mark the collected client hello as available when parsing is done and before the client hello callback */ + conn->client_hello.parsed = 1; + + /* Call client_hello_cb if exists, letting application to modify s2n_connection or swap s2n_config */ + if (conn->config->client_hello_cb) { + int rc = conn->config->client_hello_cb(conn, conn->config->client_hello_cb_ctx); + if (rc < 0) { + GUARD(s2n_queue_reader_handshake_failure_alert(conn)); + S2N_ERROR(S2N_ERR_CANCELLED); + } + if (rc) { + conn->server_name_used = 1; + } } } + if (conn->client_hello_version != S2N_SSLv2) { GUARD(s2n_process_client_hello(conn)); } diff --git a/tls/s2n_establish_session.c b/tls/s2n_establish_session.c index ef040de3985..e61b9f28502 100644 --- a/tls/s2n_establish_session.c +++ b/tls/s2n_establish_session.c @@ -40,10 +40,6 @@ int s2n_establish_session(struct s2n_connection *conn) conn->handshake.client_hello_received = 1; } - /* Next negotiate session security parameters. These could be generated, or retrieved from a cache - * based on the client's session id. This step uses data obtained from the CLIENT_HELLO message, - * which is why we process it here. - * This function won't block, it will fail and set s2n_errno accordingly. */ GUARD(s2n_conn_set_handshake_type(conn)); if (conn->client_hello_version != S2N_SSLv2) diff --git a/tls/s2n_handshake.c b/tls/s2n_handshake.c index 8aa66e268fb..efc99cf0058 100644 --- a/tls/s2n_handshake.c +++ b/tls/s2n_handshake.c @@ -27,32 +27,32 @@ #include "utils/s2n_safety.h" #include "utils/s2n_map.h" -int s2n_handshake_write_header(struct s2n_connection *conn, uint8_t message_type) +int s2n_handshake_write_header(struct s2n_stuffer *out, uint8_t message_type) { - S2N_ERROR_IF(s2n_stuffer_data_available(&conn->handshake.io), S2N_ERR_HANDSHAKE_STATE); + S2N_ERROR_IF(s2n_stuffer_data_available(out), S2N_ERR_HANDSHAKE_STATE); /* Write the message header */ - GUARD(s2n_stuffer_write_uint8(&conn->handshake.io, message_type)); + GUARD(s2n_stuffer_write_uint8(out, message_type)); /* Leave the length blank for now */ uint16_t length = 0; - GUARD(s2n_stuffer_write_uint24(&conn->handshake.io, length)); + GUARD(s2n_stuffer_write_uint24(out, length)); return 0; } -int s2n_handshake_finish_header(struct s2n_connection *conn) +int s2n_handshake_finish_header(struct s2n_stuffer *out) { - uint16_t length = s2n_stuffer_data_available(&conn->handshake.io); + uint16_t length = s2n_stuffer_data_available(out); S2N_ERROR_IF(length < TLS_HANDSHAKE_HEADER_LENGTH, S2N_ERR_SIZE_MISMATCH); uint16_t payload = length - TLS_HANDSHAKE_HEADER_LENGTH; /* Write the message header */ - GUARD(s2n_stuffer_rewrite(&conn->handshake.io)); - GUARD(s2n_stuffer_skip_write(&conn->handshake.io, 1)); - GUARD(s2n_stuffer_write_uint24(&conn->handshake.io, payload)); - GUARD(s2n_stuffer_skip_write(&conn->handshake.io, payload)); + GUARD(s2n_stuffer_rewrite(out)); + GUARD(s2n_stuffer_skip_write(out, 1)); + GUARD(s2n_stuffer_write_uint24(out, payload)); + GUARD(s2n_stuffer_skip_write(out, payload)); return 0; } @@ -68,33 +68,64 @@ int s2n_handshake_parse_header(struct s2n_connection *conn, uint8_t * message_ty return 0; } -int s2n_handshake_get_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg, struct s2n_hash_state *hash_state) +static int s2n_handshake_get_hash_state_ptr(struct s2n_connection *conn, s2n_hash_algorithm hash_alg, struct s2n_hash_state **hash_state) { switch (hash_alg) { case S2N_HASH_MD5: - *hash_state = conn->handshake.md5; + *hash_state = &conn->handshake.md5; break; case S2N_HASH_SHA1: - *hash_state = conn->handshake.sha1; + *hash_state = &conn->handshake.sha1; break; case S2N_HASH_SHA224: - *hash_state = conn->handshake.sha224; + *hash_state = &conn->handshake.sha224; break; case S2N_HASH_SHA256: - *hash_state = conn->handshake.sha256; + *hash_state = &conn->handshake.sha256; break; case S2N_HASH_SHA384: - *hash_state = conn->handshake.sha384; + *hash_state = &conn->handshake.sha384; break; case S2N_HASH_SHA512: - *hash_state = conn->handshake.sha512; + *hash_state = &conn->handshake.sha512; break; case S2N_HASH_MD5_SHA1: - *hash_state = conn->handshake.md5_sha1; + *hash_state = &conn->handshake.md5_sha1; break; default: S2N_ERROR(S2N_ERR_HASH_INVALID_ALGORITHM); + break; } + + return 0; +} + +int s2n_handshake_reset_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg) +{ + struct s2n_hash_state *hash_state_ptr = NULL; + GUARD(s2n_handshake_get_hash_state_ptr(conn, hash_alg, &hash_state_ptr)); + + GUARD(s2n_hash_reset(hash_state_ptr)); + + return 0; +} + +/* Copy the current hash state into the caller supplied pointer. + * NOTE: If the underlying digest implementation is using the EVP API + * then a pointer to the EVP ctx and md is copied. So you are actually + * taking a reference, not a value. + * Before using the hash_state returned by this function you must + * use s2n_hash_copy() to avoid modifying the underlying value. + */ +int s2n_handshake_get_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg, struct s2n_hash_state *hash_state) +{ + notnull_check(hash_state); + + struct s2n_hash_state *hash_state_ptr = NULL; + GUARD(s2n_handshake_get_hash_state_ptr(conn, hash_alg, &hash_state_ptr)); + + *hash_state = *hash_state_ptr; + return 0; } diff --git a/tls/s2n_handshake.h b/tls/s2n_handshake.h index f7aa6c3837e..3545ac762ec 100644 --- a/tls/s2n_handshake.h +++ b/tls/s2n_handshake.h @@ -27,6 +27,23 @@ #include "crypto/s2n_certificate.h" #include "crypto/s2n_hash.h" +/* From RFC 8446: https://tools.ietf.org/html/rfc8446#appendix-B.3 */ +#define TLS_HELLO_REQUEST 0 +#define TLS_CLIENT_HELLO 1 +#define TLS_SERVER_HELLO 2 +#define TLS_SERVER_NEW_SESSION_TICKET 4 +#define TLS_ENCRYPTED_EXTENSIONS 8 +#define TLS_CERTIFICATE 11 +#define TLS_SERVER_KEY 12 +#define TLS_CERT_REQ 13 +#define TLS_SERVER_HELLO_DONE 14 +#define TLS_CERT_VERIFY 15 +#define TLS_CLIENT_KEY 16 +#define TLS_FINISHED 20 +#define TLS_SERVER_CERT_STATUS 22 +#define TLS_SERVER_SESSION_LOOKUP 23 +#define TLS_MESSAGE_HASH 254 + /* This is the list of message types that we support */ typedef enum { CLIENT_HELLO=0, @@ -48,6 +65,7 @@ typedef enum { /* TLS1.3 message types. Defined: https://tools.ietf.org/html/rfc8446#appendix-B.3 */ ENCRYPTED_EXTENSIONS, SERVER_CERT_VERIFY, + HELLO_RETRY_MSG, APPLICATION_DATA, } message_type_t; @@ -157,6 +175,9 @@ struct s2n_handshake { #define WITH_SESSION_TICKET 0x20 #define IS_ISSUING_NEW_SESSION_TICKET( type ) ( (type) & WITH_SESSION_TICKET ) +/* A HelloRetryRequest was needed to proceed with the handshake */ +#define HELLO_RETRY_REQUEST 0x80 + /* Which handshake message number are we processing */ int message_number; @@ -173,6 +194,10 @@ extern int s2n_handshake_require_all_hashes(struct s2n_handshake *handshake); extern uint8_t s2n_handshake_is_hash_required(struct s2n_handshake *handshake, s2n_hash_algorithm hash_alg); extern int s2n_conn_update_required_handshake_hashes(struct s2n_connection *conn); extern int s2n_handshake_get_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg, struct s2n_hash_state *hash_state); +extern int s2n_handshake_reset_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg); extern int s2n_conn_find_name_matching_certs(struct s2n_connection *conn); extern int s2n_create_wildcard_hostname(struct s2n_stuffer *hostname, struct s2n_stuffer *output); struct s2n_cert_chain_and_key *s2n_get_compatible_cert_chain_and_key(struct s2n_connection *conn, const s2n_pkey_type cert_type); +int s2n_conn_post_handshake_hashes_update(struct s2n_connection *conn); +int s2n_conn_pre_handshake_hashes_update(struct s2n_connection *conn); +int s2n_conn_update_handshake_hashes(struct s2n_connection *conn, struct s2n_blob *data); diff --git a/tls/s2n_handshake_io.c b/tls/s2n_handshake_io.c index 2d240896992..e4c49b05514 100755 --- a/tls/s2n_handshake_io.c +++ b/tls/s2n_handshake_io.c @@ -39,21 +39,6 @@ #include "utils/s2n_random.h" #include "utils/s2n_str.h" -/* From RFC 8446: https://tools.ietf.org/html/rfc8446#appendix-B.3 */ -#define TLS_HELLO_REQUEST 0 -#define TLS_CLIENT_HELLO 1 -#define TLS_SERVER_HELLO 2 -#define TLS_SERVER_NEW_SESSION_TICKET 4 -#define TLS_ENCRYPTED_EXTENSIONS 8 -#define TLS_CERTIFICATE 11 -#define TLS_SERVER_KEY 12 -#define TLS_CERT_REQ 13 -#define TLS_SERVER_HELLO_DONE 14 -#define TLS_CERT_VERIFY 15 -#define TLS_CLIENT_KEY 16 -#define TLS_FINISHED 20 -#define TLS_SERVER_CERT_STATUS 22 -#define TLS_SERVER_SESSION_LOOKUP 23 struct s2n_handshake_action { uint8_t record_type; @@ -92,6 +77,7 @@ static struct s2n_handshake_action tls13_state_machine[] = { /* message_type_t = {Record type, Message type, Writer, {Server handler, client handler} } */ [CLIENT_HELLO] = {TLS_HANDSHAKE, TLS_CLIENT_HELLO, 'C', {s2n_establish_session, s2n_client_hello_send}}, [SERVER_HELLO] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_send, s2n_server_hello_recv}}, + [HELLO_RETRY_MSG] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_retry_send, s2n_server_hello_retry_recv}}, [ENCRYPTED_EXTENSIONS] = {TLS_HANDSHAKE, TLS_ENCRYPTED_EXTENSIONS, 'S', {s2n_encrypted_extensions_send, s2n_encrypted_extensions_recv}}, [SERVER_CERT_REQ] = {TLS_HANDSHAKE, TLS_CERT_REQ, 'S', {s2n_tls13_cert_req_send, s2n_tls13_cert_req_recv}}, [SERVER_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'S', {s2n_server_cert_send, s2n_server_cert_recv}}, @@ -133,7 +119,7 @@ static const char *message_names[] = { }; /* Maximum number of valid handshakes */ -#define S2N_HANDSHAKES_COUNT 128 +#define S2N_HANDSHAKES_COUNT 256 /* Maximum number of messages in a handshake */ #define S2N_MAX_HANDSHAKE_LENGTH 32 @@ -375,6 +361,33 @@ static message_type_t tls13_handshakes[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_L APPLICATION_DATA }, + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT ] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_FINISHED, + APPLICATION_DATA + }, + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, @@ -388,6 +401,7 @@ static message_type_t tls13_handshakes[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_L CLIENT_CHANGE_CIPHER_SPEC, CLIENT_CERT, CLIENT_FINISHED, APPLICATION_DATA }, + }; #define MAX_HANDSHAKE_TYPE_LEN 128 @@ -400,7 +414,8 @@ static const char* handshake_type_names[] = { "OCSP_STATUS|", "CLIENT_AUTH|", "WITH_SESSION_TICKET|", - "NO_CLIENT_CERT|" + "NO_CLIENT_CERT|", + "HELLO_RETRY_REQUEST|" }; #define IS_TLS13_HANDSHAKE( conn ) ((conn)->actual_protocol_version == S2N_TLS13) @@ -489,10 +504,31 @@ int s2n_generate_new_client_session_id(struct s2n_connection *conn) return 0; } +/* Lets the server flag whether a HelloRetryRequest is needed while processing extensions */ +int s2n_set_hello_retry_required(struct s2n_connection *conn) +{ + conn->handshake.handshake_type |= HELLO_RETRY_REQUEST; + + return 0; +} + +/* Lets the server determine whether a HelloRetryRequest should be sent. + * A retry is only possible after the first ClientHello (HELLO_RETRY_MSG). */ +bool s2n_is_hello_retry_required(struct s2n_connection *conn) +{ + return conn->handshake.handshake_type & HELLO_RETRY_REQUEST; +} + int s2n_conn_set_handshake_type(struct s2n_connection *conn) { + if (conn->handshake.handshake_type & HELLO_RETRY_REQUEST) { + conn->handshake.handshake_type = HELLO_RETRY_REQUEST; + } else { + conn->handshake.handshake_type = INITIAL; + } + /* A handshake type has been negotiated */ - conn->handshake.handshake_type = NEGOTIATED; + conn->handshake.handshake_type |= NEGOTIATED; s2n_cert_auth_type client_cert_auth_type; GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); @@ -629,104 +665,6 @@ const char *s2n_connection_get_handshake_type_name(struct s2n_connection *conn) return handshake_type_str[handshake_type]; } -static int s2n_conn_update_handshake_hashes(struct s2n_connection *conn, struct s2n_blob *data) -{ - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_MD5)) { - /* The handshake MD5 hash state will fail the s2n_hash_is_available() check - * since MD5 is not permitted in FIPS mode. This check will not be used as - * the handshake MD5 hash state is specifically used by the TLS 1.0 and TLS 1.1 - * PRF, which is required to comply with the TLS 1.0 and 1.1 RFCs and is approved - * as per NIST Special Publication 800-52 Revision 1. - */ - GUARD(s2n_hash_update(&conn->handshake.md5, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA1)) { - GUARD(s2n_hash_update(&conn->handshake.sha1, data->data, data->size)); - } - - const uint8_t md5_sha1_required = (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_MD5) && - s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA1)); - - if (md5_sha1_required) { - /* The MD5_SHA1 hash can still be used for TLS 1.0 and 1.1 in FIPS mode for - * the handshake hashes. This will only be used for the signature check in the - * CertificateVerify message and the PRF. NIST SP 800-52r1 approves use - * of MD5_SHA1 for these use cases (see footnotes 15 and 20, and section - * 3.3.2) */ - GUARD(s2n_hash_update(&conn->handshake.md5_sha1, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA224)) { - GUARD(s2n_hash_update(&conn->handshake.sha224, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA256)) { - GUARD(s2n_hash_update(&conn->handshake.sha256, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA384)) { - GUARD(s2n_hash_update(&conn->handshake.sha384, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA512)) { - GUARD(s2n_hash_update(&conn->handshake.sha512, data->data, data->size)); - } - - return 0; -} - -/* this hook runs before hashes are updated */ -static int s2n_conn_pre_handshake_hashes_update(struct s2n_connection *conn) -{ - if (conn->actual_protocol_version < S2N_TLS13) { - return 0; - } - - /* Right now this function is only concerned with CLIENT_FINISHED */ - if (s2n_conn_get_current_message_type(conn) != CLIENT_FINISHED) { - return 0; - } - - /* This runs before handshake update because application secrets uses only - * handshake hashes up to Server finished. This handler works in both - * read and write modes. - */ - GUARD(s2n_tls13_handle_application_secrets(conn)); - - return 0; -} - -/* this hook runs after hashes are updated */ -static int s2n_conn_post_handshake_hashes_update(struct s2n_connection *conn) -{ - if (conn->actual_protocol_version < S2N_TLS13) { - return 0; - } - - struct s2n_blob client_seq = {.data = conn->secure.client_sequence_number,.size = sizeof(conn->secure.client_sequence_number) }; - struct s2n_blob server_seq = {.data = conn->secure.server_sequence_number,.size = sizeof(conn->secure.server_sequence_number) }; - - switch(s2n_conn_get_current_message_type(conn)) { - case SERVER_HELLO: - GUARD(s2n_tls13_handle_handshake_secrets(conn)); - GUARD(s2n_blob_zero(&client_seq)); - GUARD(s2n_blob_zero(&server_seq)); - conn->server = &conn->secure; - conn->client = &conn->secure; - GUARD(s2n_stuffer_wipe(&conn->alert_in)); - break; - case CLIENT_FINISHED: - /* Reset sequence numbers for Application Data */ - GUARD(s2n_blob_zero(&client_seq)); - GUARD(s2n_blob_zero(&server_seq)); - break; - default: - break; - } - return 0; -} - /* Writing is relatively straight forward, simply write each message out as a record, * we may fragment a message across multiple records, but we never coalesce multiple * messages into single records. @@ -743,11 +681,11 @@ static int s2n_handshake_write_io(struct s2n_connection *conn) */ if (s2n_stuffer_is_wiped(&conn->handshake.io)) { if (record_type == TLS_HANDSHAKE) { - GUARD(s2n_handshake_write_header(conn, ACTIVE_STATE(conn).message_type)); + GUARD(s2n_handshake_write_header(&conn->handshake.io, ACTIVE_STATE(conn).message_type)); } GUARD(ACTIVE_STATE(conn).handler[conn->mode] (conn)); if (record_type == TLS_HANDSHAKE) { - GUARD(s2n_handshake_finish_header(conn)); + GUARD(s2n_handshake_finish_header(&conn->handshake.io)); } } diff --git a/tls/s2n_handshake_transcript.c b/tls/s2n_handshake_transcript.c new file mode 100644 index 00000000000..6a5d27df5a9 --- /dev/null +++ b/tls/s2n_handshake_transcript.c @@ -0,0 +1,176 @@ +/* + * 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.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13_handshake.h" + +#include "stuffer/s2n_stuffer.h" + +#include "utils/s2n_blob.h" + +/* Length of the synthetic message header */ +#define MESSAGE_HASH_HEADER_LENGTH 4 + +/* this hook runs after hashes are updated */ +int s2n_conn_post_handshake_hashes_update(struct s2n_connection *conn) +{ + notnull_check(conn); + + if (conn->actual_protocol_version < S2N_TLS13) { + return 0; + } + + struct s2n_blob client_seq = {.data = conn->secure.client_sequence_number,.size = sizeof(conn->secure.client_sequence_number) }; + struct s2n_blob server_seq = {.data = conn->secure.server_sequence_number,.size = sizeof(conn->secure.server_sequence_number) }; + + switch(s2n_conn_get_current_message_type(conn)) { + case HELLO_RETRY_MSG: + /* If we are sending a retry request, we didn't decide on a key share. There are no secrets to handle. */ + break; + case SERVER_HELLO: + GUARD(s2n_tls13_handle_handshake_secrets(conn)); + GUARD(s2n_blob_zero(&client_seq)); + GUARD(s2n_blob_zero(&server_seq)); + conn->server = &conn->secure; + conn->client = &conn->secure; + GUARD(s2n_stuffer_wipe(&conn->alert_in)); + break; + case CLIENT_FINISHED: + /* Reset sequence numbers for Application Data */ + GUARD(s2n_blob_zero(&client_seq)); + GUARD(s2n_blob_zero(&server_seq)); + break; + default: + break; + } + return 0; +} + +/* this hook runs before hashes are updated */ +int s2n_conn_pre_handshake_hashes_update(struct s2n_connection *conn) +{ + notnull_check(conn); + + if (conn->actual_protocol_version < S2N_TLS13) { + return 0; + } + + /* Right now this function is only concerned with CLIENT_FINISHED */ + if (s2n_conn_get_current_message_type(conn) != CLIENT_FINISHED) { + return 0; + } + + /* This runs before handshake update because application secrets uses only + * handshake hashes up to Server finished. This handler works in both + * read and write modes. + */ + GUARD(s2n_tls13_handle_application_secrets(conn)); + + return 0; +} + +int s2n_conn_update_handshake_hashes(struct s2n_connection *conn, struct s2n_blob *data) +{ + notnull_check(conn); + notnull_check(data); + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_MD5)) { + /* The handshake MD5 hash state will fail the s2n_hash_is_available() check + * since MD5 is not permitted in FIPS mode. This check will not be used as + * the handshake MD5 hash state is specifically used by the TLS 1.0 and TLS 1.1 + * PRF, which is required to comply with the TLS 1.0 and 1.1 RFCs and is approved + * as per NIST Special Publication 800-52 Revision 1. + */ + GUARD(s2n_hash_update(&conn->handshake.md5, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA1)) { + GUARD(s2n_hash_update(&conn->handshake.sha1, data->data, data->size)); + } + + const uint8_t md5_sha1_required = (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_MD5) && + s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA1)); + + if (md5_sha1_required) { + /* The MD5_SHA1 hash can still be used for TLS 1.0 and 1.1 in FIPS mode for + * the handshake hashes. This will only be used for the signature check in the + * CertificateVerify message and the PRF. NIST SP 800-52r1 approves use + * of MD5_SHA1 for these use cases (see footnotes 15 and 20, and section + * 3.3.2) */ + GUARD(s2n_hash_update(&conn->handshake.md5_sha1, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA224)) { + GUARD(s2n_hash_update(&conn->handshake.sha224, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA256)) { + GUARD(s2n_hash_update(&conn->handshake.sha256, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA384)) { + GUARD(s2n_hash_update(&conn->handshake.sha384, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA512)) { + GUARD(s2n_hash_update(&conn->handshake.sha512, data->data, data->size)); + } + + return 0; +} + +/* When a HelloRetryRequest message is used, the hash transcript needs to be recreated. + * This is done with a synthetic message header, and the hash of ClientHello1. + * + * https://tools.ietf.org/html/rfc8446#section-4.4.1 + */ +int s2n_server_hello_retry_recreate_transcript(struct s2n_connection *conn) +{ + notnull_check(conn); + + s2n_tls13_connection_keys(keys, conn); + uint8_t hash_digest_length = keys.size; + + /* Create the MessageHash (our synthetic message) */ + uint8_t msghdr[MESSAGE_HASH_HEADER_LENGTH] = {0}; + msghdr[0] = TLS_MESSAGE_HASH; + msghdr[MESSAGE_HASH_HEADER_LENGTH - 1] = hash_digest_length; + + /* Grab the current transcript hash to use as the ClientHello1 value. */ + struct s2n_hash_state hash_state, client_hello1_hash; + uint8_t client_hello1_digest_out[S2N_MAX_DIGEST_LEN]; + GUARD(s2n_handshake_get_hash_state(conn, keys.hash_algorithm, &hash_state)); + + GUARD(s2n_hash_new(&client_hello1_hash)); + GUARD(s2n_hash_copy(&client_hello1_hash, &hash_state)); + GUARD(s2n_hash_digest(&client_hello1_hash, client_hello1_digest_out, hash_digest_length)); + GUARD(s2n_hash_free(&client_hello1_hash)); + + /* Step 1: Reset the hash state */ + GUARD(s2n_handshake_reset_hash_state(conn, keys.hash_algorithm)); + + /* Step 2: Update the transcript with the synthetic message */ + struct s2n_blob msg_blob = {0}; + GUARD(s2n_blob_init(&msg_blob, msghdr, MESSAGE_HASH_HEADER_LENGTH)); + GUARD(s2n_conn_update_handshake_hashes(conn, &msg_blob)); + + /* Step 3: Update the transcript with the ClientHello1 hash */ + GUARD(s2n_blob_init(&msg_blob, client_hello1_digest_out, hash_digest_length)); + GUARD(s2n_conn_update_handshake_hashes(conn, &msg_blob)); + + return 0; +} + diff --git a/tls/s2n_server_extensions.c b/tls/s2n_server_extensions.c index 80f086b8114..4d0aa36365b 100644 --- a/tls/s2n_server_extensions.c +++ b/tls/s2n_server_extensions.c @@ -26,6 +26,7 @@ #include "tls/s2n_cipher_suites.h" #include "tls/extensions/s2n_certificate_extensions.h" +#include "tls/extensions/s2n_cookie.h" #include "tls/extensions/s2n_server_renegotiation_info.h" #include "tls/extensions/s2n_server_alpn.h" #include "tls/extensions/s2n_server_status_request.h" @@ -85,6 +86,8 @@ int s2n_server_extensions_send(struct s2n_connection *conn, struct s2n_stuffer * GUARD(s2n_extensions_server_supported_versions_send(conn, out)); /* Write key share extension */ GUARD(s2n_extensions_server_key_share_send(conn, out)); + /* Write cookie extension */ + GUARD(s2n_extensions_cookie_send(conn, out)); return 0; } @@ -172,6 +175,11 @@ int s2n_server_extensions_recv(struct s2n_connection *conn, struct s2n_blob *ext GUARD(s2n_extensions_server_key_share_recv(conn, &extension)); } break; + case TLS_EXTENSION_COOKIE: + if (s2n_is_tls13_enabled()) { + GUARD(s2n_extensions_cookie_recv(conn, &extension)); + } + break; } } diff --git a/tls/s2n_server_hello.c b/tls/s2n_server_hello.c index 88d57b17226..c5f5d6e5177 100644 --- a/tls/s2n_server_hello.c +++ b/tls/s2n_server_hello.c @@ -29,6 +29,7 @@ #include "tls/s2n_tls.h" #include "tls/s2n_tls13.h" #include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13_handshake.h" #include "stuffer/s2n_stuffer.h" @@ -90,8 +91,10 @@ static int s2n_server_add_downgrade_mechanism(struct s2n_connection *conn) { return 0; } -int s2n_server_hello_recv(struct s2n_connection *conn) +static int s2n_server_hello_parse(struct s2n_connection *conn) { + notnull_check(conn); + struct s2n_stuffer *in = &conn->handshake.io; uint8_t compression_method; uint8_t session_id_len; @@ -118,19 +121,13 @@ int s2n_server_hello_recv(struct s2n_connection *conn) S2N_ERROR_IF(extensions_size > s2n_stuffer_data_available(in), S2N_ERR_BAD_MESSAGE); struct s2n_blob extensions = {0}; - GUARD(s2n_blob_init(&extensions, s2n_stuffer_raw_read(in, extensions.size), extensions_size)); + GUARD(s2n_blob_init(&extensions, s2n_stuffer_raw_read(in, extensions_size), extensions_size)); notnull_check(extensions.data); GUARD(s2n_server_extensions_recv(conn, &extensions)); } if (conn->server_protocol_version >= S2N_TLS13) { - /* verify if it is a hello retry request*/ - if (s2n_is_hello_retry_req(conn)) { - GUARD(s2n_server_hello_retry_recv(conn)); - } - - /* Check echoed session ID matches */ S2N_ERROR_IF(session_id_len != conn->session_id_len || memcmp(session_id, conn->session_id, session_id_len), S2N_ERR_BAD_MESSAGE); conn->actual_protocol_version = conn->server_protocol_version; GUARD(s2n_set_cipher_as_client(conn, cipher_suite_wire)); @@ -173,6 +170,34 @@ int s2n_server_hello_recv(struct s2n_connection *conn) } } + return 0; +} + +/* Lets the client determine whether a HelloRetryRequest is valid */ +static bool s2n_server_hello_retry_is_valid(struct s2n_connection *conn) +{ + notnull_check(conn); + + bool has_versions_ext = conn->server_protocol_version >= S2N_TLS13; + bool has_correct_random = (memcmp(hello_retry_req_random, conn->secure.server_random, S2N_TLS_RANDOM_DATA_LEN) == 0); + + return has_versions_ext && has_correct_random && conn->client_protocol_version == S2N_TLS13; +} + +int s2n_server_hello_recv(struct s2n_connection *conn) +{ + notnull_check(conn); + + /* Read the message off the wire */ + GUARD(s2n_server_hello_parse(conn)); + + /* If this is a HelloRetryRequest, we don't process the ServerHello. + * Instead we proceed with retry logic. */ + if (s2n_server_hello_retry_is_valid(conn)) { + GUARD(s2n_server_hello_retry_recv(conn)); + return 0; + } + conn->actual_protocol_version_established = 1; GUARD(s2n_conn_set_handshake_type(conn)); @@ -190,11 +215,33 @@ int s2n_server_hello_recv(struct s2n_connection *conn) return 0; } +int s2n_server_hello_write_message(struct s2n_connection *conn) +{ + notnull_check(conn); + + /* The actual_protocol_version is set while processing the CLIENT_HELLO message, so + * it could be S2N_TLS13. SERVER_HELLO should always respond with the legacy version. + * https://tools.ietf.org/html/rfc8446#section-4.1.3 */ + const uint16_t legacy_protocol_version = MIN(conn->actual_protocol_version, S2N_TLS12); + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + protocol_version[0] = (uint8_t)(legacy_protocol_version / 10); + protocol_version[1] = (uint8_t)(legacy_protocol_version % 10); + + GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->secure.server_random, S2N_TLS_RANDOM_DATA_LEN)); + GUARD(s2n_stuffer_write_uint8(&conn->handshake.io, conn->session_id_len)); + GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->session_id, conn->session_id_len)); + GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->secure.cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); + GUARD(s2n_stuffer_write_uint8(&conn->handshake.io, S2N_TLS_COMPRESSION_METHOD_NULL)); + + return 0; +} + int s2n_server_hello_send(struct s2n_connection *conn) { - struct s2n_stuffer *out = &conn->handshake.io; + notnull_check(conn); + struct s2n_stuffer server_random = {0}; - struct s2n_blob b = {0}; GUARD(s2n_blob_init(&b, conn->secure.server_random, S2N_TLS_RANDOM_DATA_LEN)); @@ -206,24 +253,12 @@ int s2n_server_hello_send(struct s2n_connection *conn) notnull_check(rand_data.data); GUARD(s2n_get_public_random_data(&rand_data)); - /* The actual_protocol_version is set while processing the CLIENT_HELLO message, so - * it could be S2N_TLS13. SERVER_HELLO should always respond with the legacy version. - * https://tools.ietf.org/html/rfc8446#section-4.1.3 */ - uint16_t legacy_protocol_version = MIN(conn->actual_protocol_version, S2N_TLS12); - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; - protocol_version[0] = (uint8_t)(legacy_protocol_version / 10); - protocol_version[1] = (uint8_t)(legacy_protocol_version % 10); - + /* Add a downgrade detection mechanism if required */ GUARD(s2n_server_add_downgrade_mechanism(conn)); - GUARD(s2n_stuffer_write_bytes(out, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - GUARD(s2n_stuffer_write_bytes(out, conn->secure.server_random, S2N_TLS_RANDOM_DATA_LEN)); - GUARD(s2n_stuffer_write_uint8(out, conn->session_id_len)); - GUARD(s2n_stuffer_write_bytes(out, conn->session_id, conn->session_id_len)); - GUARD(s2n_stuffer_write_bytes(out, conn->secure.cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); - GUARD(s2n_stuffer_write_uint8(out, S2N_TLS_COMPRESSION_METHOD_NULL)); + GUARD(s2n_server_hello_write_message(conn)); - GUARD(s2n_server_extensions_send(conn, out)); + GUARD(s2n_server_extensions_send(conn, &conn->handshake.io)); conn->actual_protocol_version_established = 1; diff --git a/tls/s2n_server_hello_retry.c b/tls/s2n_server_hello_retry.c index 31130e60662..da5976a33b1 100644 --- a/tls/s2n_server_hello_retry.c +++ b/tls/s2n_server_hello_retry.c @@ -16,27 +16,58 @@ #include "error/s2n_errno.h" #include "utils/s2n_blob.h" +#include "tls/extensions/s2n_cookie.h" +#include "tls/s2n_cipher_suites.h" #include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" #include "utils/s2n_safety.h" +/* From RFC5246 7.4.1.2. */ +#define S2N_TLS_COMPRESSION_METHOD_NULL 0 + /* from RFC: https://tools.ietf.org/html/rfc8446#section-4.1.3*/ -const uint8_t hello_retry_req_random[] = { +uint8_t hello_retry_req_random[S2N_TLS_RANDOM_DATA_LEN] = { 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C }; -inline bool s2n_is_hello_retry_req(struct s2n_connection *conn) +static int s2n_conn_reset_retry_values(struct s2n_connection *conn) { - return s2n_constant_time_equals(hello_retry_req_random, conn->secure.server_random, S2N_TLS_RANDOM_DATA_LEN); -} + notnull_check(conn); + + /* Reset handshake values */ + conn->handshake.handshake_type = INITIAL | HELLO_RETRY_REQUEST; + conn->handshake.client_hello_received = 0; + /* Reset client hello state */ + GUARD(s2n_stuffer_wipe(&conn->client_hello.raw_message)); + GUARD(s2n_stuffer_resize(&conn->client_hello.raw_message, 0)); + GUARD(s2n_client_hello_free(&conn->client_hello)); + GUARD(s2n_stuffer_growable_alloc(&conn->client_hello.raw_message, 0)); + + return 0; +} int s2n_server_hello_retry_send(struct s2n_connection *conn) { - S2N_ERROR(S2N_ERR_UNIMPLEMENTED); + notnull_check(conn); + + memcpy_check(conn->secure.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + + GUARD(s2n_server_hello_write_message(conn)); + + /* Write the extensions */ + GUARD(s2n_server_extensions_send(conn, &conn->handshake.io)); + + /* Update transcript */ + GUARD(s2n_server_hello_retry_recreate_transcript(conn)); + GUARD(s2n_conn_reset_retry_values(conn)); + + return 0; } int s2n_server_hello_retry_recv(struct s2n_connection *conn) { - S2N_ERROR(S2N_ERR_UNIMPLEMENTED); + S2N_ERROR(S2N_ERR_BAD_MESSAGE); } diff --git a/tls/s2n_tls.h b/tls/s2n_tls.h index 3da09f6643a..3d9bfc596c9 100644 --- a/tls/s2n_tls.h +++ b/tls/s2n_tls.h @@ -30,7 +30,7 @@ extern int s2n_establish_session(struct s2n_connection *conn); extern int s2n_sslv2_client_hello_recv(struct s2n_connection *conn); extern int s2n_server_hello_retry_send(struct s2n_connection *conn); extern int s2n_server_hello_retry_recv(struct s2n_connection *conn); -extern bool s2n_is_hello_retry_req(struct s2n_connection *conn); +extern int s2n_server_hello_write_message(struct s2n_connection *conn); extern int s2n_server_hello_send(struct s2n_connection *conn); extern int s2n_server_hello_recv(struct s2n_connection *conn); extern int s2n_encrypted_extensions_send_size(struct s2n_connection *conn); @@ -71,8 +71,8 @@ extern int s2n_tls13_client_finished_recv(struct s2n_connection *conn); extern int s2n_tls13_server_finished_send(struct s2n_connection *conn); extern int s2n_tls13_server_finished_recv(struct s2n_connection *conn); extern int s2n_process_client_hello(struct s2n_connection *conn); -extern int s2n_handshake_write_header(struct s2n_connection *conn, uint8_t message_type); -extern int s2n_handshake_finish_header(struct s2n_connection *conn); +extern int s2n_handshake_write_header(struct s2n_stuffer *out, uint8_t message_type); +extern int s2n_handshake_finish_header(struct s2n_stuffer *out); extern int s2n_handshake_parse_header(struct s2n_connection *conn, uint8_t * message_type, uint32_t * length); extern int s2n_read_full_record(struct s2n_connection *conn, uint8_t * record_type, int *isSSLv2); extern int s2n_recv_close_notify(struct s2n_connection *conn, s2n_blocked_status * blocked); diff --git a/tls/s2n_tls13.h b/tls/s2n_tls13.h index 311bb200de6..c983495119c 100644 --- a/tls/s2n_tls13.h +++ b/tls/s2n_tls13.h @@ -15,8 +15,16 @@ #pragma once +#include "tls/s2n_crypto.h" + +/* from RFC: https://tools.ietf.org/html/rfc8446#section-4.1.3*/ +extern uint8_t hello_retry_req_random[S2N_TLS_RANDOM_DATA_LEN]; + int s2n_is_tls13_supported(); int s2n_is_tls13_enabled(); int s2n_enable_tls13(); int s2n_disable_tls13(); bool s2n_is_valid_tls13_cipher(const uint8_t version[2]); + +bool s2n_is_hello_retry_required(struct s2n_connection *conn); +int s2n_set_hello_retry_required(struct s2n_connection *conn); diff --git a/tls/s2n_tls13_handshake.h b/tls/s2n_tls13_handshake.h index d276646db87..81d9739f54a 100644 --- a/tls/s2n_tls13_handshake.h +++ b/tls/s2n_tls13_handshake.h @@ -36,3 +36,4 @@ int s2n_tls13_keys_from_conn(struct s2n_tls13_keys *keys, struct s2n_connection int s2n_tls13_handle_handshake_secrets(struct s2n_connection *conn); int s2n_tls13_handle_application_secrets(struct s2n_connection *conn); +int s2n_server_hello_retry_recreate_transcript(struct s2n_connection *conn); diff --git a/utils/s2n_blob.c b/utils/s2n_blob.c index 2e26ff42668..c9daf5018a4 100644 --- a/utils/s2n_blob.c +++ b/utils/s2n_blob.c @@ -27,12 +27,12 @@ bool s2n_blob_is_valid(const struct s2n_blob* b) { return - b != NULL && - S2N_OBJECT_PTR_IS_READABLE(b) && - S2N_MEM_IS_READABLE(b->data,b->size) && - S2N_IMPLIES(!b->growable, b->allocated == 0) && - S2N_IMPLIES(b->growable, S2N_MEM_IS_READABLE(b->data, b->allocated)) && - S2N_IMPLIES(b->growable, b->size <= b->allocated); + b != NULL && + S2N_OBJECT_PTR_IS_READABLE(b) && + S2N_MEM_IS_READABLE(b->data,b->size) && + S2N_IMPLIES(!b->growable, b->allocated == 0) && + S2N_IMPLIES(b->growable, S2N_MEM_IS_READABLE(b->data, b->allocated)) && + S2N_IMPLIES(b->growable, b->size <= b->allocated); } int s2n_blob_init(struct s2n_blob *b, uint8_t * data, uint32_t size)