From 622bcd3d85e612b0342d277ab57228682af9502a Mon Sep 17 00:00:00 2001 From: Doug Chapman <54039637+dougch@users.noreply.github.com> Date: Wed, 29 May 2024 14:57:33 -0700 Subject: [PATCH 1/8] [Nix] adjust pytest retrys (#4558) --- CMakeLists.txt | 2 +- tests/integrationv2/test_happy_path.py | 2 ++ tests/integrationv2/test_record_padding.py | 2 ++ tests/integrationv2/test_renegotiate.py | 7 +++++-- tests/integrationv2/test_session_resumption.py | 2 ++ 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fff7607a01f..84387a1ea2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -582,7 +582,7 @@ if (BUILD_TESTING) add_test(NAME ${test_target} COMMAND pytest - -x -n=${N} --maxfail=1 --reruns=0 --cache-clear -rpfsq + -x -n=${N} --reruns=2 --durations=10 --cache-clear -rpfsq -o log_cli=true --log-cli-level=DEBUG --provider-version=$ENV{S2N_LIBCRYPTO} --provider-criterion=off --fips-mode=0 --no-pq=0 ${test_file_path} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integrationv2 diff --git a/tests/integrationv2/test_happy_path.py b/tests/integrationv2/test_happy_path.py index 30d6fbc94cb..02549b89945 100644 --- a/tests/integrationv2/test_happy_path.py +++ b/tests/integrationv2/test_happy_path.py @@ -1,4 +1,5 @@ import copy +import platform import pytest from configuration import available_ports, ALL_TEST_CIPHERS, ALL_TEST_CURVES, ALL_TEST_CERTS, PROTOCOLS @@ -66,6 +67,7 @@ def test_s2n_server_happy_path(managed_process, cipher, provider, curve, protoco cipher.name)) in server_results.stdout +@pytest.mark.flaky(reruns=5, reruns_delay=2, condition=platform.machine().startswith("aarch")) @pytest.mark.uncollect_if(func=invalid_test_parameters) @pytest.mark.parametrize("cipher", ALL_TEST_CIPHERS, ids=get_parameter_name) @pytest.mark.parametrize("provider", [S2N, OpenSSL, GnuTLS, SSLv3Provider]) diff --git a/tests/integrationv2/test_record_padding.py b/tests/integrationv2/test_record_padding.py index 7753d04866d..84216c763bb 100644 --- a/tests/integrationv2/test_record_padding.py +++ b/tests/integrationv2/test_record_padding.py @@ -1,4 +1,5 @@ import copy +import platform import pytest import re @@ -123,6 +124,7 @@ def test_s2n_server_handles_padded_records(managed_process, cipher, provider, cu cipher.name)) in server_results.stdout +@pytest.mark.flaky(reruns=5, reruns_delay=2, condition=platform.machine().startswith("aarch")) @pytest.mark.uncollect_if(func=invalid_test_parameters) @pytest.mark.parametrize("cipher", TLS13_CIPHERS, ids=get_parameter_name) @pytest.mark.parametrize("provider", [OpenSSL]) diff --git a/tests/integrationv2/test_renegotiate.py b/tests/integrationv2/test_renegotiate.py index 6e69f446f8b..dcc448523fb 100644 --- a/tests/integrationv2/test_renegotiate.py +++ b/tests/integrationv2/test_renegotiate.py @@ -1,4 +1,5 @@ import copy +import platform import pytest import random @@ -188,13 +189,13 @@ def basic_reneg_test(managed_process, cipher, curve, certificate, protocol, prov server = managed_process(provider, server_options, send_marker=Msg.send_markers(messages, Provider.ServerMode), - timeout=5 + timeout=8 ) s2n_client = managed_process(S2N, client_options, send_marker=Msg.send_markers(messages, Provider.ClientMode), close_marker=Msg.close_marker(messages), - timeout=5 + timeout=8 ) return (s2n_client, server) @@ -292,6 +293,7 @@ def test_s2n_client_renegotiate_with_openssl(managed_process, cipher, curve, cer """ +@pytest.mark.flaky(reruns=3, reruns_delay=1, condition=platform.machine().startswith("aarch")) @pytest.mark.uncollect_if(func=invalid_test_parameters) @pytest.mark.parametrize("cipher", ALL_TEST_CIPHERS, ids=get_parameter_name) @pytest.mark.parametrize("curve", ALL_TEST_CURVES, ids=get_parameter_name) @@ -341,6 +343,7 @@ def test_s2n_client_renegotiate_with_client_auth_with_openssl(managed_process, c """ +@pytest.mark.flaky(reruns=3, reruns_delay=1, condition=platform.machine().startswith("aarch")) @pytest.mark.uncollect_if(func=invalid_test_parameters) @pytest.mark.parametrize("cipher", ALL_TEST_CIPHERS, ids=get_parameter_name) @pytest.mark.parametrize("curve", ALL_TEST_CURVES, ids=get_parameter_name) diff --git a/tests/integrationv2/test_session_resumption.py b/tests/integrationv2/test_session_resumption.py index 7813d3e7d68..2002e79aa44 100644 --- a/tests/integrationv2/test_session_resumption.py +++ b/tests/integrationv2/test_session_resumption.py @@ -1,5 +1,6 @@ import copy import os +import platform import pytest from configuration import available_ports, ALL_TEST_CIPHERS, ALL_TEST_CURVES, ALL_TEST_CERTS, PROTOCOLS, TLS13_CIPHERS @@ -243,6 +244,7 @@ def test_tls13_session_resumption_s2n_client(managed_process, cipher, curve, cer b'SSL_accept:SSLv3/TLS write certificate') == num_full_connections +@pytest.mark.flaky(reruns=7, reruns_delay=2, condition=platform.machine().startswith("aarch")) @pytest.mark.uncollect_if(func=invalid_test_parameters) @pytest.mark.parametrize("cipher", TLS13_CIPHERS, ids=get_parameter_name) @pytest.mark.parametrize("curve", ALL_TEST_CURVES, ids=get_parameter_name) From 76af4d07883a3bf938f2d111919989542d187bf5 Mon Sep 17 00:00:00 2001 From: maddeleine <59030281+maddeleine@users.noreply.github.com> Date: Wed, 29 May 2024 17:53:11 -0700 Subject: [PATCH 2/8] fix: init implicit iv for serialization feature (#4572) --- .../S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS.c | 23 ++++ .../S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS.flags | 0 tests/unit/s2n_connection_serialize_test.c | 116 +++++++++++++++--- tls/s2n_connection_serialize.c | 90 ++++++++++++++ 4 files changed, 211 insertions(+), 18 deletions(-) create mode 100644 tests/features/S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS.c create mode 100644 tests/features/S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS.flags diff --git a/tests/features/S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS.c b/tests/features/S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS.c new file mode 100644 index 00000000000..2a75230cf49 --- /dev/null +++ b/tests/features/S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS.c @@ -0,0 +1,23 @@ +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include + +int main() +{ + EVP_aead_aes_256_gcm_tls13(); + EVP_aead_aes_128_gcm_tls13(); + return 0; +} diff --git a/tests/features/S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS.flags b/tests/features/S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS.flags new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/unit/s2n_connection_serialize_test.c b/tests/unit/s2n_connection_serialize_test.c index b40a15d92b3..1f661eda220 100644 --- a/tests/unit/s2n_connection_serialize_test.c +++ b/tests/unit/s2n_connection_serialize_test.c @@ -31,16 +31,20 @@ static S2N_RESULT s2n_test_key_update(struct s2n_connection *client_conn, struct /* One side initiates key update */ RESULT_GUARD_POSIX(s2n_connection_request_key_update(client_conn, S2N_KEY_UPDATE_NOT_REQUESTED)); - /* Sending and receiving is successful after key update */ - EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); + /* Sending and receiving multiple times is successful after key update */ + for (size_t i = 0; i < 10; i++) { + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); + } EXPECT_EQUAL(client_conn->send_key_updated, 1); EXPECT_EQUAL(server_conn->recv_key_updated, 1); /* Other side initiates key update */ EXPECT_SUCCESS(s2n_connection_request_key_update(server_conn, S2N_KEY_UPDATE_NOT_REQUESTED)); - /* Sending and receiving is successful after key update */ - EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + /* Sending and receiving multiple times is successful after key update */ + for (size_t i = 0; i < 10; i++) { + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + } EXPECT_EQUAL(client_conn->recv_key_updated, 1); EXPECT_EQUAL(server_conn->send_key_updated, 1); @@ -189,6 +193,11 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server_conn), S2N_TLS12); + uint8_t iana_value[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_cipher_iana_value(server_conn, &iana_value[0], &iana_value[1])); + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_connection_serialization_length(server_conn, &length)); uint8_t buffer[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); @@ -197,9 +206,6 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_blob_init(&blob, buffer, sizeof(buffer))); struct s2n_stuffer stuffer = { 0 }; EXPECT_SUCCESS(s2n_stuffer_init_written(&stuffer, &blob)); - - uint32_t length = 0; - EXPECT_SUCCESS(s2n_connection_serialization_length(server_conn, &length)); EXPECT_EQUAL(length, s2n_stuffer_data_available(&stuffer)); uint64_t serialized_version = 0; @@ -213,8 +219,7 @@ int main(int argc, char **argv) uint8_t cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); - EXPECT_BYTEARRAY_EQUAL(cipher_suite, server_conn->secure->cipher_suite->iana_value, - S2N_TLS_CIPHER_SUITE_LEN); + EXPECT_BYTEARRAY_EQUAL(cipher_suite, iana_value, S2N_TLS_CIPHER_SUITE_LEN); uint8_t client_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, client_sequence_number, @@ -269,6 +274,11 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server_conn), S2N_TLS13); + uint8_t iana_value[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_cipher_iana_value(server_conn, &iana_value[0], &iana_value[1])); + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_connection_serialization_length(server_conn, &length)); uint8_t buffer[S2N_SERIALIZED_CONN_TLS13_SHA256_SIZE] = { 0 }; EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); @@ -277,9 +287,6 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_blob_init(&blob, buffer, sizeof(buffer))); struct s2n_stuffer stuffer = { 0 }; EXPECT_SUCCESS(s2n_stuffer_init_written(&stuffer, &blob)); - - uint32_t length = 0; - EXPECT_SUCCESS(s2n_connection_serialization_length(server_conn, &length)); EXPECT_EQUAL(length, s2n_stuffer_data_available(&stuffer)); uint64_t serialized_version = 0; @@ -293,8 +300,7 @@ int main(int argc, char **argv) uint8_t cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); - EXPECT_BYTEARRAY_EQUAL(cipher_suite, server_conn->secure->cipher_suite->iana_value, - S2N_TLS_CIPHER_SUITE_LEN); + EXPECT_BYTEARRAY_EQUAL(cipher_suite, iana_value, S2N_TLS_CIPHER_SUITE_LEN); uint8_t client_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, client_sequence_number, @@ -366,6 +372,76 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_recv(server_conn, &recv_buf, sizeof(recv_buf), &blocked)); EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); }; + + /* Cannot send or recv after serialization */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + uint8_t buffer[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + const uint8_t send_data[] = "hello world"; + EXPECT_FAILURE_WITH_ERRNO(s2n_send(server_conn, send_data, sizeof(send_data), &blocked), S2N_ERR_CLOSED); + + uint8_t recv_data = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, &recv_data, sizeof(recv_data), &blocked), S2N_ERR_CLOSED); + + /* Calling shutdown after serialization will cause a plaintext alert to be sent + * since serialization wipes the encryption context. */ + EXPECT_SUCCESS(s2n_shutdown_send(server_conn, &blocked)); + + const uint8_t expected_alert[] = { + TLS_ALERT, + S2N_TLS12 / 10, + S2N_TLS12 % 10, + 0, 2, + S2N_TLS_ALERT_LEVEL_WARNING, S2N_TLS_ALERT_CLOSE_NOTIFY + }; + uint8_t actual_alert[sizeof(expected_alert)] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&io_pair.client_in, actual_alert, sizeof(actual_alert))); + EXPECT_BYTEARRAY_EQUAL(actual_alert, expected_alert, sizeof(expected_alert)); + } + + /* Cannot serialize after connection has closed */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_shutdown_send(server_conn, &blocked)); + + /* Serializing after the connection closes will cause an error */ + uint8_t buffer[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialize(server_conn, buffer, sizeof(buffer)), S2N_ERR_CLOSED); + } }; /* s2n_connection_deserialize */ @@ -465,8 +541,10 @@ int main(int argc, char **argv) EXPECT_OK(s2n_connections_set_io_stuffer_pair(new_client_conn, server_conn, &io_pair)); /* Client can send and recv as usual */ - EXPECT_OK(s2n_send_and_recv_test(server_conn, new_client_conn)); - EXPECT_OK(s2n_send_and_recv_test(new_client_conn, server_conn)); + for (size_t idx = 0; idx < 1000; idx++) { + EXPECT_OK(s2n_send_and_recv_test(server_conn, new_client_conn)); + EXPECT_OK(s2n_send_and_recv_test(new_client_conn, server_conn)); + } }; /* Self-talk: Server can be serialized and deserialized and continue sending and receiving data @@ -507,8 +585,10 @@ int main(int argc, char **argv) EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, new_server_conn, &io_pair)); /* Server can send and recv as usual */ - EXPECT_OK(s2n_send_and_recv_test(new_server_conn, client_conn)); - EXPECT_OK(s2n_send_and_recv_test(client_conn, new_server_conn)); + for (size_t idx = 0; idx < 1000; idx++) { + EXPECT_OK(s2n_send_and_recv_test(new_server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, new_server_conn)); + } }; /* Self-talk: Test interaction between TLS1.2 session resumption and serialization */ diff --git a/tls/s2n_connection_serialize.c b/tls/s2n_connection_serialize.c index 1f959ed502c..f21088bb51e 100644 --- a/tls/s2n_connection_serialize.c +++ b/tls/s2n_connection_serialize.c @@ -15,9 +15,19 @@ #include "tls/s2n_connection_serialize.h" +#include "crypto/s2n_sequence.h" #include "tls/s2n_connection.h" #include "tls/s2n_tls13_key_schedule.h" +static bool s2n_libcrypto_supports_evp_aead_tls(void) +{ +#ifdef S2N_LIBCRYPTO_SUPPORTS_EVP_AEAD_TLS + return true; +#else + return false; +#endif +} + int s2n_connection_serialization_length(struct s2n_connection *conn, uint32_t *length) { POSIX_ENSURE_REF(conn); @@ -85,6 +95,11 @@ int s2n_connection_serialize(struct s2n_connection *conn, uint8_t *buffer, uint3 /* This method must be called after negotiation */ POSIX_ENSURE(s2n_handshake_is_complete(conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE); + /* The connection must not be closed already. Otherwise, we might have an alert + * queued up that would be sent in cleartext after we disable encryption. */ + s2n_io_status status = S2N_IO_FULL_DUPLEX; + POSIX_ENSURE(s2n_connection_check_io_status(conn, status), S2N_ERR_CLOSED); + /* Best effort check for pending input or output data. * This method should not be called until the application has stopped sending and receiving. * Saving partial read or partial write state would complicate this problem. @@ -119,6 +134,17 @@ int s2n_connection_serialize(struct s2n_connection *conn, uint8_t *buffer, uint3 POSIX_GUARD_RESULT(s2n_connection_serialize_secrets(conn, &output)); } + /* Users should not be able to send/recv on the connection after serialization as that + * could lead to nonce reuse. We close the connection to prevent the application from sending + * more application data. However, the application could still send a close_notify alert record + * to shutdown the connection, so we also intentionally wipe keys and disable encryption. + * + * A plaintext close_notify alert is not a security concern, although the peer will likely consider + * it an error. + */ + POSIX_GUARD_RESULT(s2n_connection_set_closed(conn)); + POSIX_GUARD_RESULT(s2n_crypto_parameters_wipe(conn->secure)); + return S2N_SUCCESS; } @@ -211,6 +237,68 @@ static S2N_RESULT s2n_connection_deserialize_parse(uint8_t *buffer, uint32_t buf return S2N_RESULT_OK; } +/* Boringssl and AWS-LC do a special check in tls13 during the first call to encrypt after + * initialization. In the first call they assume that the sequence number will be 0, and therefore + * the provided nonce is equivalent to the implicit IV because 0 ^ iv = iv. The recovered implicit IV + * is stored and used later on to ensure the monotonicity of sequence numbers. + * + * In the case of deserialization, in the first call the sequence number may not be 0. + * Therefore the provided nonce cannot be considered to be the implicit IV because n ^ iv != iv. + * This inability to get the correct implicit IV causes issues with encryption later on. + * + * To resolve this we preform one throwaway encryption call with a zero sequence number after + * deserialization. This allows the libcrypto to recover the implicit IV correctly. + */ +static S2N_RESULT s2n_initialize_implicit_iv(struct s2n_connection *conn, struct s2n_connection_deserialize *parsed_values) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(parsed_values); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->server); + RESULT_ENSURE_REF(conn->client); + + if (!s2n_libcrypto_supports_evp_aead_tls()) { + return S2N_RESULT_OK; + } + + uint8_t *seq_num = parsed_values->server_sequence_number; + uint8_t *implicit_iv = conn->server->server_implicit_iv; + struct s2n_session_key key = conn->server->server_key; + if (conn->mode == S2N_CLIENT) { + seq_num = parsed_values->client_sequence_number; + implicit_iv = conn->client->client_implicit_iv; + key = conn->client->client_key; + } + + uint64_t parsed_sequence_num = 0; + struct s2n_blob seq_num_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&seq_num_blob, seq_num, S2N_TLS_SEQUENCE_NUM_LEN)); + RESULT_GUARD_POSIX(s2n_sequence_number_to_uint64(&seq_num_blob, &parsed_sequence_num)); + + /* we don't need to initialize the context when the sequence number is 0 */ + if (parsed_sequence_num == 0) { + return S2N_RESULT_OK; + } + + uint8_t in_data[S2N_TLS_GCM_TAG_LEN] = { 0 }; + struct s2n_blob in_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&in_blob, in_data, sizeof(in_data))); + + struct s2n_blob iv_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&iv_blob, implicit_iv, S2N_TLS13_FIXED_IV_LEN)); + + struct s2n_blob aad_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&aad_blob, NULL, 0)); + + RESULT_ENSURE_REF(conn->secure->cipher_suite); + RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg); + RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg->cipher); + RESULT_GUARD_POSIX(conn->secure->cipher_suite->record_alg->cipher->io.aead.encrypt(&key, + &iv_blob, &aad_blob, &in_blob, &in_blob)); + + return S2N_RESULT_OK; +} + static S2N_RESULT s2n_restore_tls13_secrets(struct s2n_connection *conn, struct s2n_connection_deserialize *parsed_values) { RESULT_ENSURE_REF(conn); @@ -226,6 +314,8 @@ static S2N_RESULT s2n_restore_tls13_secrets(struct s2n_connection *conn, struct RESULT_GUARD(s2n_tls13_key_schedule_set_key(conn, S2N_MASTER_SECRET, S2N_SERVER)); RESULT_GUARD(s2n_tls13_key_schedule_set_key(conn, S2N_MASTER_SECRET, S2N_CLIENT)); + RESULT_GUARD(s2n_initialize_implicit_iv(conn, parsed_values)); + return S2N_RESULT_OK; } From 3c9a802d63d137b51bbcdc4489012a5fe21b311a Mon Sep 17 00:00:00 2001 From: toidiu Date: Mon, 3 Jun 2024 11:03:38 -0700 Subject: [PATCH 3/8] docs: document s2n_cert_auth_type behavior (#4454) --- api/s2n.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/api/s2n.h b/api/s2n.h index f30d59876a0..b4772922665 100644 --- a/api/s2n.h +++ b/api/s2n.h @@ -2291,7 +2291,22 @@ S2N_API extern int s2n_shutdown_send(struct s2n_connection *conn, s2n_blocked_st /** * Used to declare what type of client certificate authentication to use. * - * Currently the default for s2n-tls is for neither the server side or the client side to use Client (aka Mutual) authentication. + * A s2n_connection will enforce client certificate authentication (mTLS) differently based on + * the `s2n_cert_auth_type` and `s2n_mode` (client/server) of the connection, as described below. + * + * Server behavior: + * - None (default): Will not request client authentication. + * - Optional: Request the client's certificate and validate it. If no certificate is received then + * no validation is performed. + * - Required: Request the client's certificate and validate it. Abort the handshake if a client + * certificate is not received. + * + * Client behavior: + * - None: Abort the handshake if the server requests client authentication. + * - Optional (default): Sends the client certificate if the server requests client + * authentication. No certificate is sent if the application hasn't provided a certificate. + * - Required: Send the client certificate. Abort the handshake if the server doesn't request + * client authentication or if the application hasn't provided a certificate. */ typedef enum { S2N_CERT_AUTH_NONE, From 14c0ad565004fe2db6fef578e67cca1c21ea613c Mon Sep 17 00:00:00 2001 From: maddeleine <59030281+maddeleine@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:28:00 -0700 Subject: [PATCH 4/8] feat: Configurable blinding (#4562) --- api/s2n.h | 26 +++++++++ bindings/rust/s2n-tls/src/config.rs | 6 ++ tests/unit/s2n_connection_blinding_test.c | 67 +++++++++++++++++++++++ tls/s2n_config.c | 10 ++++ tls/s2n_config.h | 4 ++ tls/s2n_connection.c | 46 +++++++++++++++- tls/s2n_connection.h | 3 + 7 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 tests/unit/s2n_connection_blinding_test.c diff --git a/api/s2n.h b/api/s2n.h index b4772922665..4ebf27c3e68 100644 --- a/api/s2n.h +++ b/api/s2n.h @@ -1973,6 +1973,32 @@ S2N_API extern int s2n_connection_set_blinding(struct s2n_connection *conn, s2n_ */ S2N_API extern uint64_t s2n_connection_get_delay(struct s2n_connection *conn); +/** + * Configures the maximum blinding delay enforced after errors. + * + * Blinding protects your application from timing side channel attacks like Lucky13. While s2n-tls + * implements other, more specific mitigations for known timing side channels, blinding is important + * as a defense against currently unknown or unreported timing attacks. + * + * Setting a maximum delay lower than the recommended default (30s) will make timing attacks against + * your application easier. The lower you set the delay, the fewer requests and less total time an + * attacker will require to execute an attack. If you must lower the delay for reasons such as client + * timeouts, then you should choose the highest value practically possible to limit your risk. + * + * If you lower the blinding delay, you should also consider implementing monitoring and filtering + * to detect and reject suspicious traffic that could be gathering timing information from a potential + * side channel. Timing attacks usually involve repeatedly triggering TLS errors. + * + * @warning Do NOT set a lower blinding delay unless you understand the risks and have other + * mitigations for timing side channels in place. + * + * @param config The config object being updated. + * @param seconds The maximum number of seconds that s2n-tls will delay for in the event of a + * sensitive error. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. + */ +S2N_API extern int s2n_config_set_max_blinding_delay(struct s2n_config *config, uint32_t seconds); + /** * Sets the cipher preference override for the s2n_connection. Calling this function is not necessary * unless you want to set the cipher preferences on the connection to something different than what is in the s2n_config. diff --git a/bindings/rust/s2n-tls/src/config.rs b/bindings/rust/s2n-tls/src/config.rs index 394ee349ccc..15485a0a73b 100644 --- a/bindings/rust/s2n-tls/src/config.rs +++ b/bindings/rust/s2n-tls/src/config.rs @@ -746,6 +746,12 @@ impl Builder { Ok(self) } + /// Sets a configurable blinding delay instead of the default + pub fn set_max_blinding_delay(&mut self, seconds: u32) -> Result<&mut Self, Error> { + unsafe { s2n_config_set_max_blinding_delay(self.as_mut_ptr(), seconds).into_result() }?; + Ok(self) + } + pub fn build(mut self) -> Result { if self.load_system_certs { unsafe { diff --git a/tests/unit/s2n_connection_blinding_test.c b/tests/unit/s2n_connection_blinding_test.c new file mode 100644 index 00000000000..333e97b0468 --- /dev/null +++ b/tests/unit/s2n_connection_blinding_test.c @@ -0,0 +1,67 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#define ONE_S INT64_C(1000000000) + +S2N_RESULT s2n_connection_calculate_blinding(struct s2n_connection *conn, int64_t *min, int64_t *max); + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + struct { + uint32_t custom_blinding; + uint64_t expected_min; + uint64_t expected_max; + } test_cases[] = { + { .custom_blinding = 0, .expected_min = 0, .expected_max = 0 }, + { .custom_blinding = 1, .expected_min = 333333333, .expected_max = 1000000000 }, + { .custom_blinding = 3, .expected_min = 1000000000, .expected_max = 3000000000 }, + { .custom_blinding = 30, .expected_min = S2N_DEFAULT_BLINDING_MIN * ONE_S, .expected_max = S2N_DEFAULT_BLINDING_MAX * ONE_S }, + { .custom_blinding = UINT32_MAX, .expected_min = 1431655765000000000, .expected_max = 4294967295000000000 }, + }; + + /* s2n_connection_calculate_blinding */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(conn->config); + + int64_t min = 0; + int64_t max = 0; + + /* The default max blinding delay is 10-30 seconds */ + EXPECT_OK(s2n_connection_calculate_blinding(conn, &min, &max)); + EXPECT_EQUAL(min, S2N_DEFAULT_BLINDING_MIN * ONE_S); + EXPECT_EQUAL(max, S2N_DEFAULT_BLINDING_MAX * ONE_S); + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + EXPECT_SUCCESS(s2n_config_set_max_blinding_delay(conn->config, test_cases[i].custom_blinding)); + + min = 0; + max = 0; + EXPECT_OK(s2n_connection_calculate_blinding(conn, &min, &max)); + + EXPECT_EQUAL(min, test_cases[i].expected_min); + EXPECT_EQUAL(max, test_cases[i].expected_max); + } + } + + END_TEST(); + return 0; +} diff --git a/tls/s2n_config.c b/tls/s2n_config.c index b595a52c3e3..e188cc0db5f 100644 --- a/tls/s2n_config.c +++ b/tls/s2n_config.c @@ -1238,3 +1238,13 @@ int s2n_config_set_serialization_version(struct s2n_config *config, s2n_serializ return S2N_SUCCESS; } + +int s2n_config_set_max_blinding_delay(struct s2n_config *config, uint32_t seconds) +{ + POSIX_ENSURE_REF(config); + + config->custom_blinding_set = 1; + config->max_blinding = seconds; + + return S2N_SUCCESS; +} diff --git a/tls/s2n_config.h b/tls/s2n_config.h index 714c8a2328b..c8afd4ffeb8 100644 --- a/tls/s2n_config.h +++ b/tls/s2n_config.h @@ -105,6 +105,8 @@ struct s2n_config { /* TLS1.3 can be dangerous with kTLS. Require it to be explicitly enabled. */ unsigned ktls_tls13_enabled : 1; + unsigned custom_blinding_set : 1; + struct s2n_dh_params *dhparams; /* Needed until we can deprecate s2n_config_add_cert_chain_and_key. This is * used to release memory allocated only in the deprecated API that the application @@ -123,6 +125,8 @@ struct s2n_config { s2n_client_hello_fn *client_hello_cb; s2n_client_hello_cb_mode client_hello_cb_mode; + uint32_t max_blinding; + void *client_hello_cb_ctx; uint64_t session_state_lifetime_in_nanos; diff --git a/tls/s2n_connection.c b/tls/s2n_connection.c index 807fc85ba52..874bd329bbe 100644 --- a/tls/s2n_connection.c +++ b/tls/s2n_connection.c @@ -1153,7 +1153,6 @@ int s2n_connection_set_blinding(struct s2n_connection *conn, s2n_blinding blindi } #define ONE_S INT64_C(1000000000) -#define TEN_S INT64_C(10000000000) static S2N_RESULT s2n_connection_get_delay_impl(struct s2n_connection *conn, uint64_t *delay) { @@ -1188,13 +1187,54 @@ uint64_t s2n_connection_get_delay(struct s2n_connection *conn) } } +/* s2n-tls has a random delay that will trigger for sensitive errors. This is a mitigation + * for possible timing sidechannels. + * + * The historical sidechannel that inspired s2n-tls blinding was the Lucky 13 attack, which takes + * advantage of potential timing differences when removing padding from a record encrypted in CBC mode. + * The attack is only theoretical in TLS; the attack criteria is unlikely to ever occur + * (See: Fardan, N. J. A., & Paterson, K. G. (2013, May 1). Lucky Thirteen: Breaking the TLS and + * DTLS Record Protocols.) However, we still include blinding to provide a defense in depth mitigation. + */ +S2N_RESULT s2n_connection_calculate_blinding(struct s2n_connection *conn, int64_t *min, int64_t *max) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(min); + RESULT_ENSURE_REF(max); + RESULT_ENSURE_REF(conn->config); + + /* + * The default delay is a random value between 10-30s. The rational behind the range is that the + * floor is the fixed cost that an attacker must pay per attempt, in this case, 10s. The length of + * the range then affects the number of attempts that an attacker must perform in order to recover a + * byte of plaintext with a certain degree of confidence. + * + * A uniform distribution of the range [a, b] has a variance of ((b - a)^2)/12. Therefore, given a + * hypothetical timing difference of 1us, the number of attempts necessary to distinguish the correct + * byte from an incorrect byte in a Lucky13-style attack is (((30 - 10) * 10 ^6)^2)/12 ~= 3.3 trillion + * (note that we first have to convert from seconds to microseconds to match the unit of the timing difference.) + */ + *min = S2N_DEFAULT_BLINDING_MIN * ONE_S; + *max = S2N_DEFAULT_BLINDING_MAX * ONE_S; + + /* Setting the min to 1/3 of the max is an arbitrary ratio of fixed to variable delay. + * It is based on the ratio of our original default values. + */ + if (conn->config->custom_blinding_set) { + *max = conn->config->max_blinding * ONE_S; + *min = *max / 3; + } + + return S2N_RESULT_OK; +} + static S2N_RESULT s2n_connection_kill(struct s2n_connection *conn) { RESULT_ENSURE_REF(conn); RESULT_GUARD(s2n_connection_set_closed(conn)); - /* Delay between 10 and 30 seconds in nanoseconds */ - int64_t min = TEN_S, max = 3 * TEN_S; + int64_t min = 0, max = 0; + RESULT_GUARD(s2n_connection_calculate_blinding(conn, &min, &max)); /* Keep track of the delay so that it can be enforced */ uint64_t rand_delay = 0; diff --git a/tls/s2n_connection.h b/tls/s2n_connection.h index 58c4757cac8..d3729840cfd 100644 --- a/tls/s2n_connection.h +++ b/tls/s2n_connection.h @@ -49,6 +49,9 @@ #define is_handshake_complete(conn) (APPLICATION_DATA == s2n_conn_get_current_message_type(conn)) +#define S2N_DEFAULT_BLINDING_MAX 30 +#define S2N_DEFAULT_BLINDING_MIN 10 + typedef enum { S2N_NO_TICKET = 0, S2N_DECRYPT_TICKET, From c3fdba444c327fe16e55bfe1c8e88c90253b2351 Mon Sep 17 00:00:00 2001 From: Jou Ho <43765840+jouho@users.noreply.github.com> Date: Mon, 3 Jun 2024 15:49:43 -0700 Subject: [PATCH 5/8] refactor: add try_compile feature probe for RSA-PSS signing (#4569) --- crypto/s2n_evp_signing.c | 4 ++-- crypto/s2n_rsa_pss.h | 2 +- crypto/s2n_rsa_signing.c | 8 +++++-- crypto/s2n_rsa_signing.h | 7 ------ .../S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING.c | 23 +++++++++++++++++++ ...N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING.flags | 0 tests/unit/s2n_rsa_pss_rsae_test.c | 6 ++++- 7 files changed, 37 insertions(+), 13 deletions(-) create mode 100644 tests/features/S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING.c create mode 100644 tests/features/S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING.flags diff --git a/crypto/s2n_evp_signing.c b/crypto/s2n_evp_signing.c index 886c3b1fa3b..7d38af60751 100644 --- a/crypto/s2n_evp_signing.c +++ b/crypto/s2n_evp_signing.c @@ -41,11 +41,11 @@ static S2N_RESULT s2n_evp_md_ctx_set_pkey_ctx(EVP_MD_CTX *ctx, EVP_PKEY_CTX *pct static S2N_RESULT s2n_evp_pkey_set_rsa_pss_saltlen(EVP_PKEY_CTX *pctx) { -#if RSA_PSS_SIGNING_SUPPORTED +#if defined(S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING) RESULT_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, RSA_PSS_SALTLEN_DIGEST), S2N_ERR_PKEY_CTX_INIT); return S2N_RESULT_OK; #else - RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); + RESULT_BAIL(S2N_ERR_RSA_PSS_NOT_SUPPORTED); #endif } diff --git a/crypto/s2n_rsa_pss.h b/crypto/s2n_rsa_pss.h index b4600eff69b..8363e3aaad1 100644 --- a/crypto/s2n_rsa_pss.h +++ b/crypto/s2n_rsa_pss.h @@ -34,7 +34,7 @@ * * This feature requires this Openssl commit for Openssl 1.1.x versions: openssl/openssl@4088b92 */ -#if RSA_PSS_SIGNING_SUPPORTED && OPENSSL_VERSION_NUMBER > 0x1010104fL +#if defined(S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING) && OPENSSL_VERSION_NUMBER > 0x1010104fL #define RSA_PSS_CERTS_SUPPORTED 1 #else #define RSA_PSS_CERTS_SUPPORTED 0 diff --git a/crypto/s2n_rsa_signing.c b/crypto/s2n_rsa_signing.c index 9bec1616c13..2b7cfce59f3 100644 --- a/crypto/s2n_rsa_signing.c +++ b/crypto/s2n_rsa_signing.c @@ -118,10 +118,14 @@ int s2n_rsa_pkcs1v15_verify(const struct s2n_pkey *pub, struct s2n_hash_state *d /* this function returns whether RSA PSS signing is supported */ int s2n_is_rsa_pss_signing_supported() { - return RSA_PSS_SIGNING_SUPPORTED; +#if defined(S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING) + return 1; +#else + return 0; +#endif } -#if RSA_PSS_SIGNING_SUPPORTED +#if defined(S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING) static int s2n_evp_pkey_ctx_set_rsa_signature_digest(EVP_PKEY_CTX *ctx, const EVP_MD *digest_alg) { diff --git a/crypto/s2n_rsa_signing.h b/crypto/s2n_rsa_signing.h index a1d859d22e8..dd56d7b576a 100644 --- a/crypto/s2n_rsa_signing.h +++ b/crypto/s2n_rsa_signing.h @@ -20,13 +20,6 @@ #include "crypto/s2n_rsa.h" #include "utils/s2n_blob.h" -/* Check for libcrypto 1.1 for RSA PSS Signing and EV_Key usage */ -#if (S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 1) || defined(OPENSSL_IS_AWSLC)) && !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL) - #define RSA_PSS_SIGNING_SUPPORTED 1 -#else - #define RSA_PSS_SIGNING_SUPPORTED 0 -#endif - int s2n_rsa_pkcs1v15_sign(const struct s2n_pkey *priv, struct s2n_hash_state *digest, struct s2n_blob *signature); int s2n_rsa_pkcs1v15_verify(const struct s2n_pkey *pub, struct s2n_hash_state *digest, struct s2n_blob *signature); diff --git a/tests/features/S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING.c b/tests/features/S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING.c new file mode 100644 index 00000000000..395756c0e5e --- /dev/null +++ b/tests/features/S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING.c @@ -0,0 +1,23 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include +#include + +int main() +{ + EVP_PKEY_CTX_set_rsa_pss_saltlen(NULL, RSA_PSS_SALTLEN_DIGEST); + return 0; +} diff --git a/tests/features/S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING.flags b/tests/features/S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING.flags new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/unit/s2n_rsa_pss_rsae_test.c b/tests/unit/s2n_rsa_pss_rsae_test.c index f8ff3509757..ac91b5a3c2c 100644 --- a/tests/unit/s2n_rsa_pss_rsae_test.c +++ b/tests/unit/s2n_rsa_pss_rsae_test.c @@ -181,7 +181,11 @@ int main(int argc, char **argv) hash_state_new(sign_hash, random_msg); hash_state_new(verify_hash, random_msg); - EXPECT_EQUAL(s2n_is_rsa_pss_signing_supported(), RSA_PSS_SIGNING_SUPPORTED); +#if defined(S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING) + EXPECT_EQUAL(s2n_is_rsa_pss_signing_supported(), 1); +#else + EXPECT_EQUAL(s2n_is_rsa_pss_signing_supported(), 0); +#endif if (!s2n_is_rsa_pss_signing_supported()) { EXPECT_FAILURE_WITH_ERRNO(rsa_public_key.sign(rsa_cert_chain->private_key, S2N_SIGNATURE_RSA_PSS_RSAE, &sign_hash, &result), From 7ec1446ac1d836cefb6a5363b9bfb438a4faa792 Mon Sep 17 00:00:00 2001 From: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Tue, 4 Jun 2024 11:50:34 -0400 Subject: [PATCH 6/8] fix: Validate received signature algorithm in EVP verify (#4574) --- crypto/s2n_evp_signing.c | 16 ++++ crypto/s2n_pkey.c | 38 ++++++-- crypto/s2n_pkey.h | 1 + tests/unit/s2n_rsa_pss_rsae_test.c | 46 ++++++--- tests/unit/s2n_tls13_cert_verify_test.c | 118 ++++++++++++++++++++++++ tls/s2n_auth_selection.c | 32 ++----- tls/s2n_signature_algorithms.c | 23 +++++ tls/s2n_signature_algorithms.h | 3 + 8 files changed, 229 insertions(+), 48 deletions(-) diff --git a/crypto/s2n_evp_signing.c b/crypto/s2n_evp_signing.c index 7d38af60751..bd0b9c5972d 100644 --- a/crypto/s2n_evp_signing.c +++ b/crypto/s2n_evp_signing.c @@ -19,6 +19,7 @@ #include "crypto/s2n_pkey.h" #include "crypto/s2n_rsa_pss.h" #include "error/s2n_errno.h" +#include "tls/s2n_signature_algorithms.h" #include "utils/s2n_safety.h" DEFINE_POINTER_CLEANUP_FUNC(EVP_PKEY_CTX *, EVP_PKEY_CTX_free); @@ -97,6 +98,20 @@ static S2N_RESULT s2n_evp_signing_validate_hash_alg(s2n_signature_algorithm sig_ return S2N_RESULT_OK; } +static S2N_RESULT s2n_evp_signing_validate_sig_alg(const struct s2n_pkey *key, s2n_signature_algorithm sig_alg) +{ + RESULT_ENSURE_REF(key); + + /* Ensure that the signature algorithm type matches the key type. */ + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + RESULT_GUARD(s2n_pkey_get_type(key->pkey, &pkey_type)); + s2n_pkey_type sig_alg_type = S2N_PKEY_TYPE_UNKNOWN; + RESULT_GUARD(s2n_signature_algorithm_get_pkey_type(sig_alg, &sig_alg_type)); + RESULT_ENSURE(pkey_type == sig_alg_type, S2N_ERR_INVALID_SIGNATURE_ALGORITHM); + + return S2N_RESULT_OK; +} + int s2n_evp_sign(const struct s2n_pkey *priv, s2n_signature_algorithm sig_alg, struct s2n_hash_state *hash_state, struct s2n_blob *signature) { @@ -136,6 +151,7 @@ int s2n_evp_verify(const struct s2n_pkey *pub, s2n_signature_algorithm sig_alg, POSIX_ENSURE_REF(signature); POSIX_ENSURE(s2n_evp_signing_supported(), S2N_ERR_HASH_NOT_READY); POSIX_GUARD_RESULT(s2n_evp_signing_validate_hash_alg(sig_alg, hash_state->alg)); + POSIX_GUARD_RESULT(s2n_evp_signing_validate_sig_alg(pub, sig_alg)); DEFER_CLEANUP(EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(pub->pkey, NULL), EVP_PKEY_CTX_free_pointer); POSIX_ENSURE_REF(pctx); diff --git a/crypto/s2n_pkey.c b/crypto/s2n_pkey.c index bcc57c42422..be1ce8ff45d 100644 --- a/crypto/s2n_pkey.c +++ b/crypto/s2n_pkey.c @@ -194,6 +194,30 @@ S2N_RESULT s2n_asn1der_to_public_key_and_type(struct s2n_pkey *pub_key, return S2N_RESULT_OK; } +S2N_RESULT s2n_pkey_get_type(EVP_PKEY *evp_pkey, s2n_pkey_type *pkey_type) +{ + RESULT_ENSURE_REF(evp_pkey); + RESULT_ENSURE_REF(pkey_type); + *pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + int type = EVP_PKEY_base_id(evp_pkey); + switch (type) { + case EVP_PKEY_RSA: + *pkey_type = S2N_PKEY_TYPE_RSA; + break; + case EVP_PKEY_RSA_PSS: + *pkey_type = S2N_PKEY_TYPE_RSA_PSS; + break; + case EVP_PKEY_EC: + *pkey_type = S2N_PKEY_TYPE_ECDSA; + break; + default: + RESULT_BAIL(S2N_ERR_DECODE_CERTIFICATE); + } + + return S2N_RESULT_OK; +} + S2N_RESULT s2n_pkey_from_x509(X509 *cert, struct s2n_pkey *pub_key_out, s2n_pkey_type *pkey_type_out) { @@ -204,23 +228,19 @@ S2N_RESULT s2n_pkey_from_x509(X509 *cert, struct s2n_pkey *pub_key_out, DEFER_CLEANUP(EVP_PKEY *evp_public_key = X509_get_pubkey(cert), EVP_PKEY_free_pointer); RESULT_ENSURE(evp_public_key != NULL, S2N_ERR_DECODE_CERTIFICATE); - /* Check for success in decoding certificate according to type */ - int type = EVP_PKEY_base_id(evp_public_key); - switch (type) { - case EVP_PKEY_RSA: + RESULT_GUARD(s2n_pkey_get_type(evp_public_key, pkey_type_out)); + switch (*pkey_type_out) { + case S2N_PKEY_TYPE_RSA: RESULT_GUARD(s2n_rsa_pkey_init(pub_key_out)); RESULT_GUARD(s2n_evp_pkey_to_rsa_public_key(&pub_key_out->key.rsa_key, evp_public_key)); - *pkey_type_out = S2N_PKEY_TYPE_RSA; break; - case EVP_PKEY_RSA_PSS: + case S2N_PKEY_TYPE_RSA_PSS: RESULT_GUARD(s2n_rsa_pss_pkey_init(pub_key_out)); RESULT_GUARD(s2n_evp_pkey_to_rsa_pss_public_key(&pub_key_out->key.rsa_key, evp_public_key)); - *pkey_type_out = S2N_PKEY_TYPE_RSA_PSS; break; - case EVP_PKEY_EC: + case S2N_PKEY_TYPE_ECDSA: RESULT_GUARD(s2n_ecdsa_pkey_init(pub_key_out)); RESULT_GUARD(s2n_evp_pkey_to_ecdsa_public_key(&pub_key_out->key.ecdsa_key, evp_public_key)); - *pkey_type_out = S2N_PKEY_TYPE_ECDSA; break; default: RESULT_BAIL(S2N_ERR_DECODE_CERTIFICATE); diff --git a/crypto/s2n_pkey.h b/crypto/s2n_pkey.h index c00ad3d846d..d50a9ea2ca7 100644 --- a/crypto/s2n_pkey.h +++ b/crypto/s2n_pkey.h @@ -71,5 +71,6 @@ int s2n_pkey_free(struct s2n_pkey *pkey); S2N_RESULT s2n_asn1der_to_private_key(struct s2n_pkey *priv_key, struct s2n_blob *asn1der, int type_hint); S2N_RESULT s2n_asn1der_to_public_key_and_type(struct s2n_pkey *pub_key, s2n_pkey_type *pkey_type, struct s2n_blob *asn1der); +S2N_RESULT s2n_pkey_get_type(EVP_PKEY *evp_pkey, s2n_pkey_type *pkey_type); S2N_RESULT s2n_pkey_from_x509(X509 *cert, struct s2n_pkey *pub_key_out, s2n_pkey_type *pkey_type_out); diff --git a/tests/unit/s2n_rsa_pss_rsae_test.c b/tests/unit/s2n_rsa_pss_rsae_test.c index ac91b5a3c2c..132ffc0c5d0 100644 --- a/tests/unit/s2n_rsa_pss_rsae_test.c +++ b/tests/unit/s2n_rsa_pss_rsae_test.c @@ -245,14 +245,30 @@ int main(int argc, char **argv) /* Test: If they share the same RSA key, * an RSA cert and an RSA_PSS cert are equivalent for PSS signatures. */ { - struct s2n_pkey rsa_pss_public_key; - s2n_pkey_type rsa_pss_pkey_type; - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&rsa_pss_public_key, &rsa_pss_pkey_type, &rsa_pss_cert_chain->cert_chain->head->raw)); - EXPECT_EQUAL(rsa_pss_pkey_type, S2N_PKEY_TYPE_RSA_PSS); - - /* Set the keys equal. */ - RSA *rsa_key_copy = EVP_PKEY_get1_RSA(rsa_public_key.pkey); - POSIX_GUARD_OSSL(EVP_PKEY_set1_RSA(rsa_pss_public_key.pkey, rsa_key_copy), S2N_ERR_KEY_INIT); + DEFER_CLEANUP(struct s2n_pkey rsa_public_key_as_pss = { 0 }, s2n_pkey_free); + s2n_pkey_type rsa_public_key_as_pss_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&rsa_public_key_as_pss, &rsa_public_key_as_pss_type, + &rsa_cert_chain->cert_chain->head->raw)); + EXPECT_EQUAL(rsa_public_key_as_pss_type, S2N_PKEY_TYPE_RSA); + + DEFER_CLEANUP(struct s2n_pkey rsa_pss_public_key = { 0 }, s2n_pkey_free); + s2n_pkey_type rsa_pss_pkey_type_shared = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&rsa_pss_public_key, &rsa_pss_pkey_type_shared, + &rsa_pss_cert_chain->cert_chain->head->raw)); + EXPECT_EQUAL(rsa_pss_pkey_type_shared, S2N_PKEY_TYPE_RSA_PSS); + + /* Set the keys equal. + * + * When EVP signing APIs are enabled, s2n-tls validates the signature algorithm type + * against the EVP pkey type, so the EVP pkey type must be RSA_PSS in order to use the + * RSA_PSS signature algorithm. However, EVP_PKEY_set1_RSA sets the EVP pkey type to + * RSA, even if the EVP pkey type was RSA_PSS (there is no EVP_PKEY_set1_RSA_PSS API). + * + * To ensure that the RSA_PSS EVP pkey type remains set to RSA_PSS, the RSA key is + * overridden instead of the RSA_PSS key, since its pkey type is RSA anyway. + */ + RSA *rsa_key_copy = EVP_PKEY_get1_RSA(rsa_pss_public_key.pkey); + POSIX_GUARD_OSSL(EVP_PKEY_set1_RSA(rsa_public_key_as_pss.pkey, rsa_key_copy), S2N_ERR_KEY_INIT); RSA_free(rsa_key_copy); /* RSA signed with PSS, RSA_PSS verified with PSS */ @@ -260,8 +276,10 @@ int main(int argc, char **argv) hash_state_new(sign_hash, random_msg); hash_state_new(verify_hash, random_msg); - EXPECT_SUCCESS(rsa_public_key.sign(rsa_cert_chain->private_key, S2N_SIGNATURE_RSA_PSS_RSAE, &sign_hash, &result)); - EXPECT_SUCCESS(rsa_pss_public_key.verify(&rsa_public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &result)); + EXPECT_SUCCESS(rsa_public_key_as_pss.sign(rsa_pss_cert_chain->private_key, S2N_SIGNATURE_RSA_PSS_RSAE, + &sign_hash, &result)); + EXPECT_SUCCESS(rsa_pss_public_key.verify(&rsa_pss_public_key, S2N_SIGNATURE_RSA_PSS_PSS, + &verify_hash, &result)); }; /* RSA_PSS signed with PSS, RSA verified with PSS */ @@ -269,11 +287,11 @@ int main(int argc, char **argv) hash_state_new(sign_hash, random_msg); hash_state_new(verify_hash, random_msg); - EXPECT_SUCCESS(rsa_pss_public_key.sign(rsa_cert_chain->private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &result)); - EXPECT_SUCCESS(rsa_public_key.verify(&rsa_public_key, S2N_SIGNATURE_RSA_PSS_RSAE, &verify_hash, &result)); + EXPECT_SUCCESS(rsa_pss_public_key.sign(rsa_pss_cert_chain->private_key, S2N_SIGNATURE_RSA_PSS_PSS, + &sign_hash, &result)); + EXPECT_SUCCESS(rsa_public_key_as_pss.verify(&rsa_public_key_as_pss, S2N_SIGNATURE_RSA_PSS_RSAE, + &verify_hash, &result)); }; - - EXPECT_SUCCESS(s2n_pkey_free(&rsa_pss_public_key)); }; EXPECT_SUCCESS(s2n_pkey_free(&rsa_public_key)); diff --git a/tests/unit/s2n_tls13_cert_verify_test.c b/tests/unit/s2n_tls13_cert_verify_test.c index 395ccfbec86..7987ac4c1b4 100644 --- a/tests/unit/s2n_tls13_cert_verify_test.c +++ b/tests/unit/s2n_tls13_cert_verify_test.c @@ -234,5 +234,123 @@ int main(int argc, char **argv) run_tests(&test_cases[i], S2N_SERVER); } + /* Self-talk: Ensure that the signature algorithm used to sign the CertificateVerify message + * is validated against the certificate type + */ + if (s2n_is_tls13_fully_supported()) { + struct s2n_tls13_cert_verify_test test_server_parameters[] = { + { + .cert_file = S2N_RSA_2048_PKCS1_CERT_CHAIN, + .key_file = S2N_RSA_2048_PKCS1_KEY, + .sig_scheme = &s2n_rsa_pss_rsae_sha256, + }, + { + .cert_file = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, + .key_file = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, + .sig_scheme = &s2n_rsa_pss_pss_sha256, + }, + { + .cert_file = S2N_ECDSA_P256_PKCS1_CERT_CHAIN, + .key_file = S2N_ECDSA_P256_PKCS1_KEY, + .sig_scheme = &s2n_ecdsa_sha256, + } + }; + + const struct s2n_signature_scheme *test_client_sig_schemes[] = { + &s2n_rsa_pss_rsae_sha256, + &s2n_rsa_pss_pss_sha256, + &s2n_ecdsa_sha256, + }; + + for (size_t param_idx = 0; param_idx < s2n_array_len(test_server_parameters); param_idx++) { + struct s2n_tls13_cert_verify_test server_parameters = test_server_parameters[param_idx]; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *cert_chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&cert_chain, server_parameters.cert_file, + server_parameters.key_file)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* The server only supports the single test certificate. Due to the fallback logic in + * the s2n-tls server, the signature algorithm corresponding with the test certificate + * will always be used to sign the CertificateVerify message, regardless of the + * client's advertised signature schemes. + */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); + + for (size_t sig_idx = 0; sig_idx < s2n_array_len(test_client_sig_schemes); sig_idx++) { + 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_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + 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_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* The client only supports the single test signature scheme, which allows for the + * server to sign the CertificateVerify message with a signature algorithm that + * isn't supported by the client. + */ + const struct s2n_signature_scheme *client_advertised_sig_scheme = test_client_sig_schemes[sig_idx]; + struct s2n_signature_preferences test_sig_preferences = { + .count = 1, + .signature_schemes = &client_advertised_sig_scheme, + }; + struct s2n_security_policy client_policy = security_policy_test_all_tls13; + client_policy.signature_preferences = &test_sig_preferences; + client_conn->security_policy_override = &client_policy; + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Send the CertificateVerify message. */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_CERT_VERIFY)); + EXPECT_SUCCESS(s2n_stuffer_rewrite(&server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(server_conn)); + + /* Check that the expected signature algorithm was used by the server. */ + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme, server_parameters.sig_scheme); + + /* Overwrite the SignatureScheme field of the CertificateVerify message to lie to the + * client about which signature algorithm was used to sign the signature content. This + * will trick the client into always thinking its advertised signature algorithm was + * used. + */ + struct s2n_stuffer cert_verify_stuffer = server_conn->handshake.io; + EXPECT_SUCCESS(s2n_stuffer_rewrite(&cert_verify_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&cert_verify_stuffer, + client_advertised_sig_scheme->iana_value)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + + int ret = s2n_tls13_cert_verify_recv(client_conn); + + if (client_advertised_sig_scheme == server_parameters.sig_scheme) { + /* If the client's advertised signature scheme matches what the server actually + * used to sign the CertificateVerify message, validation should succeed. + */ + EXPECT_SUCCESS(ret); + } else { + /* Otherwise, the client should observe that the indicated signature algorithm + * from the server doesn't match the certificate type, and the connection + * should fail. + */ + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_INVALID_SIGNATURE_ALGORITHM); + } + } + } + } + END_TEST(); } diff --git a/tls/s2n_auth_selection.c b/tls/s2n_auth_selection.c index e14c587e544..477e43973db 100644 --- a/tls/s2n_auth_selection.c +++ b/tls/s2n_auth_selection.c @@ -20,6 +20,7 @@ #include "crypto/s2n_signature.h" #include "tls/s2n_cipher_suites.h" #include "tls/s2n_kex.h" +#include "tls/s2n_signature_algorithms.h" #include "utils/s2n_safety.h" /* This module should contain any logic related to choosing a valid combination of @@ -56,31 +57,12 @@ int s2n_get_auth_method_for_cert_type(s2n_pkey_type cert_type, s2n_authenticatio POSIX_BAIL(S2N_ERR_CERT_TYPE_UNSUPPORTED); } -static int s2n_get_cert_type_for_sig_alg(s2n_signature_algorithm sig_alg, s2n_pkey_type *cert_type) -{ - switch (sig_alg) { - case S2N_SIGNATURE_RSA_PSS_RSAE: - case S2N_SIGNATURE_RSA: - *cert_type = S2N_PKEY_TYPE_RSA; - return S2N_SUCCESS; - case S2N_SIGNATURE_ECDSA: - *cert_type = S2N_PKEY_TYPE_ECDSA; - return S2N_SUCCESS; - case S2N_SIGNATURE_RSA_PSS_PSS: - *cert_type = S2N_PKEY_TYPE_RSA_PSS; - return S2N_SUCCESS; - case S2N_SIGNATURE_ANONYMOUS: - POSIX_BAIL(S2N_ERR_INVALID_SIGNATURE_ALGORITHM); - } - POSIX_BAIL(S2N_ERR_INVALID_SIGNATURE_ALGORITHM); -} - static int s2n_is_sig_alg_valid_for_cipher_suite(s2n_signature_algorithm sig_alg, struct s2n_cipher_suite *cipher_suite) { POSIX_ENSURE_REF(cipher_suite); - s2n_pkey_type cert_type_for_sig_alg; - POSIX_GUARD(s2n_get_cert_type_for_sig_alg(sig_alg, &cert_type_for_sig_alg)); + s2n_pkey_type cert_type_for_sig_alg = S2N_PKEY_TYPE_UNKNOWN; + POSIX_GUARD_RESULT(s2n_signature_algorithm_get_pkey_type(sig_alg, &cert_type_for_sig_alg)); /* Non-ephemeral key exchange methods require encryption, and RSA-PSS certificates * do not support encryption. @@ -110,8 +92,8 @@ static int s2n_certs_exist_for_sig_scheme(struct s2n_connection *conn, const str { POSIX_ENSURE_REF(sig_scheme); - s2n_pkey_type cert_type; - POSIX_GUARD(s2n_get_cert_type_for_sig_alg(sig_scheme->sig_alg, &cert_type)); + s2n_pkey_type cert_type = S2N_PKEY_TYPE_UNKNOWN; + POSIX_GUARD_RESULT(s2n_signature_algorithm_get_pkey_type(sig_scheme->sig_alg, &cert_type)); /* A valid cert must exist for the authentication method. */ struct s2n_cert_chain_and_key *cert = s2n_get_compatible_cert_chain_and_key(conn, cert_type); @@ -226,8 +208,8 @@ int s2n_select_certs_for_server_auth(struct s2n_connection *conn, struct s2n_cer POSIX_ENSURE_REF(conn->handshake_params.server_cert_sig_scheme); s2n_signature_algorithm sig_alg = conn->handshake_params.server_cert_sig_scheme->sig_alg; - s2n_pkey_type cert_type = 0; - POSIX_GUARD(s2n_get_cert_type_for_sig_alg(sig_alg, &cert_type)); + s2n_pkey_type cert_type = S2N_PKEY_TYPE_UNKNOWN; + POSIX_GUARD_RESULT(s2n_signature_algorithm_get_pkey_type(sig_alg, &cert_type)); *chosen_certs = s2n_get_compatible_cert_chain_and_key(conn, cert_type); S2N_ERROR_IF(*chosen_certs == NULL, S2N_ERR_CERT_TYPE_UNSUPPORTED); diff --git a/tls/s2n_signature_algorithms.c b/tls/s2n_signature_algorithms.c index 4f3cd8895a4..a871d220356 100644 --- a/tls/s2n_signature_algorithms.c +++ b/tls/s2n_signature_algorithms.c @@ -373,3 +373,26 @@ int s2n_recv_supported_sig_scheme_list(struct s2n_stuffer *in, struct s2n_sig_sc return 0; } + +S2N_RESULT s2n_signature_algorithm_get_pkey_type(s2n_signature_algorithm sig_alg, s2n_pkey_type *pkey_type) +{ + RESULT_ENSURE_REF(pkey_type); + *pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + switch (sig_alg) { + case S2N_SIGNATURE_RSA: + case S2N_SIGNATURE_RSA_PSS_RSAE: + *pkey_type = S2N_PKEY_TYPE_RSA; + break; + case S2N_SIGNATURE_RSA_PSS_PSS: + *pkey_type = S2N_PKEY_TYPE_RSA_PSS; + break; + case S2N_SIGNATURE_ECDSA: + *pkey_type = S2N_PKEY_TYPE_ECDSA; + break; + default: + RESULT_BAIL(S2N_ERR_INVALID_SIGNATURE_ALGORITHM); + } + + return S2N_RESULT_OK; +} diff --git a/tls/s2n_signature_algorithms.h b/tls/s2n_signature_algorithms.h index 9dc95f01345..bd9f85e2e46 100644 --- a/tls/s2n_signature_algorithms.h +++ b/tls/s2n_signature_algorithms.h @@ -17,6 +17,7 @@ #include "api/s2n.h" #include "crypto/s2n_hash.h" +#include "crypto/s2n_pkey.h" #include "crypto/s2n_signature.h" #include "stuffer/s2n_stuffer.h" #include "tls/s2n_signature_scheme.h" @@ -35,3 +36,5 @@ S2N_RESULT s2n_signature_algorithm_select(struct s2n_connection *conn); int s2n_recv_supported_sig_scheme_list(struct s2n_stuffer *in, struct s2n_sig_scheme_list *sig_hash_algs); S2N_RESULT s2n_signature_algorithms_supported_list_send(struct s2n_connection *conn, struct s2n_stuffer *out); + +S2N_RESULT s2n_signature_algorithm_get_pkey_type(s2n_signature_algorithm sig_alg, s2n_pkey_type *pkey_type); From 4dae2b27e0218e1f4a4d48c5a64ce57a6c1e9dac Mon Sep 17 00:00:00 2001 From: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Tue, 4 Jun 2024 20:15:35 -0400 Subject: [PATCH 7/8] chore(bindings): release 0.2.7 (#4580) --- bindings/rust/s2n-tls-sys/templates/Cargo.template | 2 +- bindings/rust/s2n-tls-tokio/Cargo.toml | 4 ++-- bindings/rust/s2n-tls/Cargo.toml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bindings/rust/s2n-tls-sys/templates/Cargo.template b/bindings/rust/s2n-tls-sys/templates/Cargo.template index bac5d812c3d..9ff9295d7a2 100644 --- a/bindings/rust/s2n-tls-sys/templates/Cargo.template +++ b/bindings/rust/s2n-tls-sys/templates/Cargo.template @@ -1,7 +1,7 @@ [package] name = "s2n-tls-sys" description = "A C99 implementation of the TLS/SSL protocols" -version = "0.2.6" +version = "0.2.7" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" diff --git a/bindings/rust/s2n-tls-tokio/Cargo.toml b/bindings/rust/s2n-tls-tokio/Cargo.toml index f612163482c..d97a2021f5c 100644 --- a/bindings/rust/s2n-tls-tokio/Cargo.toml +++ b/bindings/rust/s2n-tls-tokio/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "s2n-tls-tokio" description = "An implementation of TLS streams for Tokio built on top of s2n-tls" -version = "0.2.6" +version = "0.2.7" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" @@ -15,7 +15,7 @@ default = [] errno = { version = "0.3" } libc = { version = "0.2" } pin-project-lite = { version = "0.2" } -s2n-tls = { version = "=0.2.6", path = "../s2n-tls" } +s2n-tls = { version = "=0.2.7", path = "../s2n-tls" } tokio = { version = "1", features = ["net", "time"] } [dev-dependencies] diff --git a/bindings/rust/s2n-tls/Cargo.toml b/bindings/rust/s2n-tls/Cargo.toml index 4f2b482507e..1e35195ccd5 100644 --- a/bindings/rust/s2n-tls/Cargo.toml +++ b/bindings/rust/s2n-tls/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "s2n-tls" description = "A C99 implementation of the TLS/SSL protocols" -version = "0.2.6" +version = "0.2.7" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" @@ -21,7 +21,7 @@ testing = ["bytes"] bytes = { version = "1", optional = true } errno = { version = "0.3" } libc = "0.2" -s2n-tls-sys = { version = "=0.2.6", path = "../s2n-tls-sys", features = ["internal"] } +s2n-tls-sys = { version = "=0.2.7", path = "../s2n-tls-sys", features = ["internal"] } pin-project-lite = "0.2" hex = "0.4" From 114ccab0ff2cde491203ac841837d0d39b767412 Mon Sep 17 00:00:00 2001 From: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Wed, 5 Jun 2024 16:14:09 -0400 Subject: [PATCH 8/8] Merge pull request from GHSA-52xf-5p2m-9wrv --- tests/unit/s2n_client_key_exchange_test.c | 525 ++++++++++++++++++++++ tls/s2n_client_key_exchange.c | 8 + 2 files changed, 533 insertions(+) create mode 100644 tests/unit/s2n_client_key_exchange_test.c diff --git a/tests/unit/s2n_client_key_exchange_test.c b/tests/unit/s2n_client_key_exchange_test.c new file mode 100644 index 00000000000..154e55b40e7 --- /dev/null +++ b/tests/unit/s2n_client_key_exchange_test.c @@ -0,0 +1,525 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_kex.h" +#include "utils/s2n_random.h" + +DEFINE_POINTER_CLEANUP_FUNC(struct s2n_async_pkey_op *, s2n_async_pkey_op_free); + +struct s2n_test_rsa_client_key_send_ctx { + bool override_premaster_secret_size; + uint32_t size; + + bool invalidate_padded_premaster_secret; + uint32_t invalidate_index; + + bool override_premaster_secret_version; + uint8_t version; +}; + +static S2N_RESULT s2n_test_rsa_pkcs1_v15_padding_encrypt(struct s2n_connection *conn, struct s2n_blob *in, + struct s2n_blob *out) +{ + /* RSAES-PKCS1-V1_5-ENCRYPT ((n, e), M) from https://www.rfc-editor.org/rfc/rfc8017#section-7.2.1 */ + + /* Input: + * (n, e) recipient's RSA public key (k denotes the length in + * octets of the modulus n) + */ + const s2n_rsa_public_key *n_e = &conn->handshake_params.server_public_key.key.rsa_key; + const int k = RSA_size(n_e->rsa); + RESULT_ENSURE_GT(k, 0); + + /* M message to be encrypted, an octet string of length + * mLen, where mLen <= k - 11 + */ + struct s2n_blob *M = in; + uint32_t mLen = M->size; + + /* Output: + * C ciphertext, an octet string of length k + */ + struct s2n_blob *C = out; + RESULT_ENSURE_GTE(C->size, k); + + /* Steps: + * 1. Length checking: If mLen > k - 11, output "message too long" + * and stop. + */ + RESULT_ENSURE_LTE(mLen, k - 11); + + /* 2. EME-PKCS1-v1_5 encoding: + * a. Generate an octet string PS of length k - mLen - 3 + * consisting of pseudo-randomly generated nonzero octets. + * The length of PS will be at least eight octets. + * b. Concatenate PS, the message M, and other padding to form + * an encoded message EM of length k octets as + * EM = 0x00 || 0x02 || PS || 0x00 || M. + */ + uint8_t EM_data[4096] = { 0 }; + struct s2n_blob EM_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&EM_blob, EM_data, sizeof(EM_data))); + struct s2n_stuffer EM_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&EM_stuffer, &EM_blob)); + + /* 0x00 || 0x02 */ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&EM_stuffer, 0x00)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&EM_stuffer, 0x02)); + + /* || PS */ + struct s2n_blob PS_blob = { 0 }; + uint32_t PS_len = k - mLen - 3; + uint8_t *PS_data = s2n_stuffer_raw_write(&EM_stuffer, PS_len); + RESULT_ENSURE_REF(PS_data); + RESULT_GUARD_POSIX(s2n_blob_init(&PS_blob, PS_data, PS_len)); + RESULT_GUARD(s2n_get_public_random_data(&PS_blob)); + + /* Ensure random bytes are nonzero */ + for (size_t i = 0; i < PS_len; i++) { + PS_data[i] = (PS_data[i] % (UINT8_MAX - 1)) + 1; + } + + /* || 0x00 */ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&EM_stuffer, 0x00)); + + /* || M */ + RESULT_GUARD_POSIX(s2n_stuffer_write(&EM_stuffer, M)); + + uint8_t *EM = s2n_stuffer_raw_read(&EM_stuffer, k); + RESULT_ENSURE_REF(EM); + RESULT_ENSURE_EQ(s2n_stuffer_data_available(&EM_stuffer), 0); + + struct s2n_test_rsa_client_key_send_ctx *ctx = s2n_connection_get_ctx(conn); + RESULT_ENSURE_REF(ctx); + if (ctx->invalidate_padded_premaster_secret) { + RESULT_ENSURE_LT(ctx->invalidate_index, k); + if (ctx->invalidate_index > 1 && ctx->invalidate_index < 2 + PS_len) { + /* PS bytes must not be 0. */ + EM[ctx->invalidate_index] = 0; + } else { + /* Otherwise bytes can be invalidated by modifying them arbitrarily. */ + EM[ctx->invalidate_index] += 1; + } + } + + /* 3. RSA encryption: + * b. Apply the RSAEP encryption primitive (Section 5.1.1) to + * the RSA public key (n, e) and the message representative m + * to produce an integer ciphertext representative c: + * c = RSAEP ((n, e), m). + */ + int r = RSA_public_encrypt(k, (unsigned char *) EM, (unsigned char *) C->data, + s2n_unsafe_rsa_get_non_const(n_e), RSA_NO_PADDING); + RESULT_ENSURE((int64_t) r == (int64_t) C->size, S2N_ERR_SIZE_MISMATCH); + + return S2N_RESULT_OK; +} + +/* More general version of s2n_rsa_client_key_send() that allows the premaster secret to be + * invalidated by configuring a s2n_test_rsa_client_key_send_ctx on the connection. + */ +static int s2n_test_rsa_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(shared_key); + + struct s2n_test_rsa_client_key_send_ctx *ctx = s2n_connection_get_ctx(conn); + POSIX_ENSURE_REF(ctx); + + uint8_t client_hello_version = s2n_connection_get_client_hello_version(conn); + if (ctx->override_premaster_secret_version) { + client_hello_version = ctx->version; + } + uint8_t client_hello_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + client_hello_protocol_version[0] = client_hello_version / 10; + client_hello_protocol_version[1] = client_hello_version % 10; + + shared_key->data = conn->secrets.version.tls12.rsa_premaster_secret; + uint32_t secret_size = S2N_TLS_SECRET_LEN; + if (ctx->override_premaster_secret_size) { + secret_size = ctx->size; + } + POSIX_ENSURE_LTE(secret_size, sizeof(conn->secrets.version.tls12.rsa_premaster_secret)); + shared_key->size = secret_size; + + POSIX_GUARD_RESULT(s2n_get_private_random_data(shared_key)); + + /* The first two bytes of the premaster secret contain the client hello version. */ + POSIX_CHECKED_MEMCPY(conn->secrets.version.tls12.rsa_premaster_secret, client_hello_protocol_version, + S2N_TLS_PROTOCOL_VERSION_LEN); + + uint32_t encrypted_size = 0; + POSIX_GUARD_RESULT(s2n_pkey_size(&conn->handshake_params.server_public_key, &encrypted_size)); + POSIX_ENSURE_LTE(encrypted_size, 0xffff); + + /* Write the length. */ + POSIX_GUARD(s2n_stuffer_write_uint16(&conn->handshake.io, encrypted_size)); + + struct s2n_blob encrypted = { 0 }; + encrypted.data = s2n_stuffer_raw_write(&conn->handshake.io, encrypted_size); + encrypted.size = encrypted_size; + POSIX_ENSURE_REF(encrypted.data); + + POSIX_GUARD_RESULT(s2n_test_rsa_pkcs1_v15_padding_encrypt(conn, shared_key, &encrypted)); + + POSIX_GUARD(s2n_pkey_free(&conn->handshake_params.server_public_key)); + + return S2N_SUCCESS; +} + +struct s2n_test_async_pkey_cb_ctx { + uint32_t async_invoked_count; + uint32_t offload_invoked_count; +}; + +static int s2n_test_async_pkey_decrypt_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(op); + + DEFER_CLEANUP(struct s2n_async_pkey_op *op_defer_cleanup = op, s2n_async_pkey_op_free_pointer); + + struct s2n_test_async_pkey_cb_ctx *ctx = s2n_connection_get_ctx(conn); + POSIX_ENSURE_REF(ctx); + ctx->async_invoked_count += 1; + + s2n_async_pkey_op_type op_type = 0; + EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(op, &op_type)); + EXPECT_EQUAL(op_type, S2N_ASYNC_DECRYPT); + + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); + EXPECT_NOT_NULL(chain_and_key); + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + EXPECT_SUCCESS(s2n_async_pkey_op_perform(op, pkey)); + EXPECT_SUCCESS(s2n_async_pkey_op_apply(op, conn)); + + return S2N_SUCCESS; +} + +static int s2n_test_offload_pkey_decrypt_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(op); + + DEFER_CLEANUP(struct s2n_async_pkey_op *op_defer_cleanup = op, s2n_async_pkey_op_free_pointer); + + struct s2n_test_async_pkey_cb_ctx *ctx = s2n_connection_get_ctx(conn); + POSIX_ENSURE_REF(ctx); + ctx->offload_invoked_count += 1; + + s2n_async_pkey_op_type op_type = 0; + EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(op, &op_type)); + EXPECT_EQUAL(op_type, S2N_ASYNC_DECRYPT); + + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); + EXPECT_NOT_NULL(chain_and_key); + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + uint8_t input_data[4096] = { 0 }; + struct s2n_blob input_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&input_blob, input_data, sizeof(input_data))); + + uint32_t input_size = 0; + EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); + EXPECT_TRUE(input_size <= input_blob.size); + EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input_data, sizeof(input_data))); + input_blob.size = input_size; + + uint8_t output_data[S2N_TLS_SECRET_LEN] = { 0 }; + struct s2n_blob output_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&output_blob, output_data, sizeof(output_data))); + + EXPECT_SUCCESS(s2n_pkey_decrypt(pkey, &input_blob, &output_blob)); + EXPECT_SUCCESS(s2n_async_pkey_op_set_output(op, output_blob.data, output_blob.size)); + EXPECT_SUCCESS(s2n_async_pkey_op_apply(op, conn)); + + return S2N_SUCCESS; +} + +typedef enum { + S2N_PKEY_TEST_DEFAULT, + S2N_PKEY_TEST_ASYNC, + S2N_PKEY_TEST_OFFLOAD, + S2N_PKEY_TEST_COUNT, +} s2n_pkey_test_mode; + +static S2N_RESULT s2n_validate_test_async_pkey_ctx(struct s2n_test_async_pkey_cb_ctx *ctx, s2n_pkey_test_mode pkey_mode) +{ + RESULT_ENSURE_REF(ctx); + + switch (pkey_mode) { + case S2N_PKEY_TEST_DEFAULT: + RESULT_ENSURE_EQ(ctx->async_invoked_count, 0); + RESULT_ENSURE_EQ(ctx->offload_invoked_count, 0); + break; + case S2N_PKEY_TEST_ASYNC: + RESULT_ENSURE_EQ(ctx->async_invoked_count, 1); + RESULT_ENSURE_EQ(ctx->offload_invoked_count, 0); + break; + case S2N_PKEY_TEST_OFFLOAD: + RESULT_ENSURE_EQ(ctx->async_invoked_count, 0); + RESULT_ENSURE_EQ(ctx->offload_invoked_count, 1); + break; + default: + RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT); + } + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test: same error + location for all Bleichenbacher attack cases. + * + * This doesn't prove safety, since Bleichenbacher is a timing side-channel attack. But if this + * test DOES fail, we likely have an issue. + */ + for (size_t pkey_test_mode = 0; pkey_test_mode < S2N_PKEY_TEST_COUNT; pkey_test_mode++) { + /* We must use an RSA cert so that we can test RSA kex */ + DEFER_CLEANUP(struct s2n_cert_chain_and_key *rsa_cert_chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_cert_chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + struct s2n_pkey *rsa_private_key = s2n_cert_chain_and_key_get_private_key(rsa_cert_chain); + EXPECT_NOT_NULL(rsa_private_key); + uint32_t key_size = 0; + EXPECT_OK(s2n_pkey_size(rsa_private_key, &key_size)); + EXPECT_TRUE(key_size > 0); + + struct s2n_kex rsa_kex = s2n_rsa; + rsa_kex.client_key_send = s2n_test_rsa_client_key_send; + + struct s2n_cipher_suite rsa_kex_cipher_suite = s2n_rsa_with_aes_128_gcm_sha256; + rsa_kex_cipher_suite.key_exchange_alg = &rsa_kex; + + struct s2n_cipher_suite *rsa_kex_cipher_suites[1] = { &rsa_kex_cipher_suite }; + struct s2n_cipher_preferences rsa_kex_cipher_pref = { + .suites = rsa_kex_cipher_suites, + .count = 1, + }; + struct s2n_security_policy test_rsa_policy = security_policy_test_all; + test_rsa_policy.cipher_preferences = &rsa_kex_cipher_pref; + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + client_config->security_policy = &test_rsa_policy; + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert_chain)); + server_config->security_policy = &test_rsa_policy; + + if (pkey_test_mode == S2N_PKEY_TEST_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, s2n_test_async_pkey_decrypt_callback)); + } else if (pkey_test_mode == S2N_PKEY_TEST_OFFLOAD) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, s2n_test_offload_pkey_decrypt_callback)); + } + + /* Sanity check: ensure s2n_test_rsa_client_key_send sends a valid premaster secret by default */ + for (size_t i = 0; i < 100; i++) { + struct s2n_test_rsa_client_key_send_ctx rsa_send_ctx = { 0 }; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + EXPECT_SUCCESS(s2n_connection_set_ctx(client, &rsa_send_ctx)); + + struct s2n_test_async_pkey_cb_ctx async_pkey_ctx = { 0 }; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + EXPECT_SUCCESS(s2n_connection_set_ctx(server, &async_pkey_ctx)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + EXPECT_OK(s2n_validate_test_async_pkey_ctx(&async_pkey_ctx, pkey_test_mode)); + } + + /* All the tests for the Bleichenbacher attack cases should result in the same error from + * s2n_aead_cipher_aes_gcm.c. However, the line number of the error can change depending on + * how s2n-tls is built. s2n_sterror_source() is called from the first test case to + * determine the expected error source for the remaining tests. + */ + int expected_error = S2N_ERR_DECRYPT; + const char *expected_source_str = NULL; + + /* Test: client sends invalid premaster secret */ + { + struct s2n_test_rsa_client_key_send_ctx ctx = { + .invalidate_padded_premaster_secret = true, + .invalidate_index = key_size - 1, + }; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + EXPECT_SUCCESS(s2n_connection_set_ctx(client, &ctx)); + + struct s2n_test_async_pkey_cb_ctx async_pkey_ctx = { 0 }; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + EXPECT_SUCCESS(s2n_connection_set_ctx(server, &async_pkey_ctx)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_negotiate_test_server_and_client(server, client), expected_error); + expected_source_str = s2n_strerror_source(s2n_errno); + + /* Ensure that the error came from s2n_aead_cipher_aes_gcm.c. */ + EXPECT_NOT_NULL(strstr(expected_source_str, "s2n_aead_cipher_aes_gcm.c")); + + EXPECT_OK(s2n_validate_test_async_pkey_ctx(&async_pkey_ctx, pkey_test_mode)); + } + + /* Test: wrong version */ + for (uint8_t version = 0; version < S2N_TLS13 + 10; version++) { + struct s2n_test_rsa_client_key_send_ctx rsa_send_ctx = { + .override_premaster_secret_version = true, + .version = version, + }; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + EXPECT_SUCCESS(s2n_connection_set_ctx(client, &rsa_send_ctx)); + + struct s2n_test_async_pkey_cb_ctx async_pkey_ctx = { 0 }; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + EXPECT_SUCCESS(s2n_connection_set_ctx(server, &async_pkey_ctx)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + if (version == S2N_TLS12) { + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + } else { + EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_negotiate_test_server_and_client(server, client), + expected_error); + EXPECT_STRING_EQUAL(s2n_strerror_source(s2n_errno), expected_source_str); + } + + EXPECT_OK(s2n_validate_test_async_pkey_ctx(&async_pkey_ctx, pkey_test_mode)); + } + + /* Test: wrong plaintext size */ + for (uint32_t size = 0; size <= S2N_TLS_SECRET_LEN; size++) { + struct s2n_test_rsa_client_key_send_ctx ctx = { + .override_premaster_secret_size = true, + .size = size, + }; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + EXPECT_SUCCESS(s2n_connection_set_ctx(client, &ctx)); + + struct s2n_test_async_pkey_cb_ctx async_pkey_ctx = { 0 }; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + EXPECT_SUCCESS(s2n_connection_set_ctx(server, &async_pkey_ctx)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + if (size == S2N_TLS_SECRET_LEN) { + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + } else { + EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_negotiate_test_server_and_client(server, client), + expected_error); + EXPECT_STRING_EQUAL(s2n_strerror_source(s2n_errno), expected_source_str); + } + + EXPECT_OK(s2n_validate_test_async_pkey_ctx(&async_pkey_ctx, pkey_test_mode)); + } + + /* Test: wrong padding + * + * Each of the padding bytes are invalidated before encrypting, and the resulting + * ciphertext is sent to the server. PKCS1 v1.5 padding starts at 0 and ends before the + * plaintext. + */ + for (uint32_t invalidate_index = 0; invalidate_index < key_size - S2N_TLS_SECRET_LEN; invalidate_index++) { + struct s2n_test_rsa_client_key_send_ctx ctx = { + .invalidate_padded_premaster_secret = true, + .invalidate_index = invalidate_index, + }; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + EXPECT_SUCCESS(s2n_connection_set_ctx(client, &ctx)); + + struct s2n_test_async_pkey_cb_ctx async_pkey_ctx = { 0 }; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + EXPECT_SUCCESS(s2n_connection_set_ctx(server, &async_pkey_ctx)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_negotiate_test_server_and_client(server, client), expected_error); + EXPECT_STRING_EQUAL(s2n_strerror_source(s2n_errno), expected_source_str); + + EXPECT_OK(s2n_validate_test_async_pkey_ctx(&async_pkey_ctx, pkey_test_mode)); + } + } + + END_TEST(); +} diff --git a/tls/s2n_client_key_exchange.c b/tls/s2n_client_key_exchange.c index 29114df7035..303cece9483 100644 --- a/tls/s2n_client_key_exchange.c +++ b/tls/s2n_client_key_exchange.c @@ -165,6 +165,14 @@ int s2n_rsa_client_key_recv_complete(struct s2n_connection *conn, bool rsa_faile conn->handshake.rsa_failed |= !s2n_constant_time_equals(client_hello_protocol_version, conn->secrets.version.tls12.rsa_premaster_secret, S2N_TLS_PROTOCOL_VERSION_LEN); + /* Required to protect against Bleichenbacher attack. + * See https://www.rfc-editor.org/rfc/rfc5246#section-7.4.7.1 + * We choose the first option: always setting the version in the rsa_premaster_secret + * from our local view of the client_hello value. + */ + conn->secrets.version.tls12.rsa_premaster_secret[0] = client_hello_protocol_version[0]; + conn->secrets.version.tls12.rsa_premaster_secret[1] = client_hello_protocol_version[1]; + return 0; }