From b36e5b5924aa5bae6012f2b2331ee84d9779ec18 Mon Sep 17 00:00:00 2001 From: Wesley Rosenblum <55108558+WesleyRosenblum@users.noreply.github.com> Date: Thu, 1 Aug 2024 13:02:35 -0700 Subject: [PATCH 01/19] chore(bindings): release 0.2.10 (#4683) --- bindings/rust/s2n-tls-hyper/Cargo.toml | 5 +++-- bindings/rust/s2n-tls-sys/templates/Cargo.template | 2 +- bindings/rust/s2n-tls-tokio/Cargo.toml | 4 ++-- bindings/rust/s2n-tls/Cargo.toml | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/bindings/rust/s2n-tls-hyper/Cargo.toml b/bindings/rust/s2n-tls-hyper/Cargo.toml index f29f27e0c93..bba1ec7b885 100644 --- a/bindings/rust/s2n-tls-hyper/Cargo.toml +++ b/bindings/rust/s2n-tls-hyper/Cargo.toml @@ -7,13 +7,14 @@ edition = "2021" rust-version = "1.63.0" repository = "https://github.com/aws/s2n-tls" license = "Apache-2.0" +publish = false [features] default = [] [dependencies] -s2n-tls = { version = "=0.2.9", path = "../s2n-tls" } -s2n-tls-tokio = { version = "=0.2.9", path = "../s2n-tls-tokio" } +s2n-tls = { version = "=0.2.10", path = "../s2n-tls" } +s2n-tls-tokio = { version = "=0.2.10", path = "../s2n-tls-tokio" } hyper = { version = "1" } hyper-util = { version = "0.1", features = ["client-legacy", "tokio", "http1"] } tower-service = { version = "0.3" } diff --git a/bindings/rust/s2n-tls-sys/templates/Cargo.template b/bindings/rust/s2n-tls-sys/templates/Cargo.template index 530d50576b4..37ae6bfc870 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.9" +version = "0.2.10" 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 4783c41c896..d7fd23d96b5 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.9" +version = "0.2.10" 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.9", path = "../s2n-tls" } +s2n-tls = { version = "=0.2.10", 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 e10757cc1e2..ce70bbf6de1 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.9" +version = "0.2.10" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" @@ -21,7 +21,7 @@ unstable-testing = [] [dependencies] errno = { version = "0.3" } libc = "0.2" -s2n-tls-sys = { version = "=0.2.9", path = "../s2n-tls-sys", features = ["internal"] } +s2n-tls-sys = { version = "=0.2.10", path = "../s2n-tls-sys", features = ["internal"] } pin-project-lite = "0.2" hex = "0.4" From 0685abc65f2e51b09748feb7f6ccbf4836c8422d Mon Sep 17 00:00:00 2001 From: Lindsay Stewart Date: Thu, 1 Aug 2024 22:46:23 -0700 Subject: [PATCH 02/19] fix: don't fail for 0 blinding delay (#4671) --- bin/common.c | 3 +++ bin/s2nc.c | 6 ++++++ bin/s2nd.c | 3 +++ tests/unit/s2n_safety_blinding_test.c | 20 ++++++++++++++++++++ tls/s2n_connection.c | 3 +++ 5 files changed, 35 insertions(+) diff --git a/bin/common.c b/bin/common.c index ff19ace9da6..66c3459f245 100644 --- a/bin/common.c +++ b/bin/common.c @@ -314,6 +314,9 @@ int s2n_setup_external_psk_list(struct s2n_connection *conn, char *psk_optarg_li int s2n_set_common_server_config(int max_early_data, struct s2n_config *config, struct conn_settings conn_settings, const char *cipher_prefs, const char *session_ticket_key_file_path) { + /* The s2n-tls blinding security feature is disabled for testing purposes to make debugging easier. */ + GUARD_EXIT(s2n_config_set_max_blinding_delay(config, 0), "Error setting blinding delay"); + GUARD_EXIT(s2n_config_set_server_max_early_data_size(config, max_early_data), "Error setting max early data"); GUARD_EXIT(s2n_config_add_dhparams(config, dhparams), "Error adding DH parameters"); diff --git a/bin/s2nc.c b/bin/s2nc.c index d96e094026d..5781948e2e3 100644 --- a/bin/s2nc.c +++ b/bin/s2nc.c @@ -76,6 +76,9 @@ const char default_trusted_cert[] = void usage() { /* clang-format off */ + fprintf(stderr, "s2nc is an s2n-tls client testing utility.\n"); + fprintf(stderr, "It is not intended for production use.\n"); + fprintf(stderr, "\n"); fprintf(stderr, "usage: s2nc [options] host [port]\n"); fprintf(stderr, " host: hostname or IP address to connect to\n"); fprintf(stderr, " port: port to connect to\n"); @@ -201,6 +204,9 @@ static void setup_s2n_config(struct s2n_config *config, const char *cipher_prefs exit(1); } + /* The s2n-tls blinding security feature is disabled for testing purposes to make debugging easier. */ + GUARD_EXIT(s2n_config_set_max_blinding_delay(config, 0), "Error setting blinding delay"); + GUARD_EXIT(s2n_config_set_cipher_preferences(config, cipher_prefs), "Error setting cipher prefs"); GUARD_EXIT(s2n_config_set_status_request_type(config, type), "OCSP validation is not supported by the linked libCrypto implementation. It cannot be set."); diff --git a/bin/s2nd.c b/bin/s2nd.c index 886c50b3e35..77733899e7a 100644 --- a/bin/s2nd.c +++ b/bin/s2nd.c @@ -138,6 +138,9 @@ static char default_private_key[] = void usage() { /* clang-format off */ + fprintf(stderr, "s2nd is an s2n-tls server testing utility.\n"); + fprintf(stderr, "It is not intended for production use.\n"); + fprintf(stderr, "\n"); fprintf(stderr, "usage: s2nd [options] host port\n"); fprintf(stderr, " host: hostname or IP address to listen on\n"); fprintf(stderr, " port: port to listen on\n"); diff --git a/tests/unit/s2n_safety_blinding_test.c b/tests/unit/s2n_safety_blinding_test.c index aab11f28888..15305ea9c00 100644 --- a/tests/unit/s2n_safety_blinding_test.c +++ b/tests/unit/s2n_safety_blinding_test.c @@ -151,6 +151,26 @@ int main(int argc, char **argv) EXPECT_BLINDING(conn); }; + /* Skips blinding if set to 0 */ + { + SETUP_TEST(conn); + s2n_errno = S2N_ERR_UNIMPLEMENTED; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* No blinding delay, but closed connection */ + EXPECT_SUCCESS(s2n_config_set_max_blinding_delay(config, 0)); + EXPECT_OK(s2n_connection_apply_error_blinding(&conn)); + EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + + /* Any non-zero blinding delay still causes blinding */ + EXPECT_SUCCESS(s2n_config_set_max_blinding_delay(config, 1)); + EXPECT_OK(s2n_connection_apply_error_blinding(&conn)); + EXPECT_BLINDING(conn); + } + EXPECT_SUCCESS(s2n_connection_free(conn)); }; diff --git a/tls/s2n_connection.c b/tls/s2n_connection.c index fa218f9ba7e..1f2d2db8348 100644 --- a/tls/s2n_connection.c +++ b/tls/s2n_connection.c @@ -1238,6 +1238,9 @@ static S2N_RESULT s2n_connection_kill(struct s2n_connection *conn) int64_t min = 0, max = 0; RESULT_GUARD(s2n_connection_calculate_blinding(conn, &min, &max)); + if (max == 0) { + return S2N_RESULT_OK; + } /* Keep track of the delay so that it can be enforced */ uint64_t rand_delay = 0; From aacb0ab373af272e817c0d7e822eef87983fbb9f Mon Sep 17 00:00:00 2001 From: Lindsay Stewart Date: Fri, 2 Aug 2024 00:56:52 -0700 Subject: [PATCH 03/19] test(cbmc): add stuffer hex proofs (#4659) --- .../cbmc/proofs/s2n_stuffer_read_hex/Makefile | 40 ++++++++++ .../s2n_stuffer_read_hex/cbmc-proof.txt | 1 + .../s2n_stuffer_read_hex_harness.c | 65 ++++++++++++++++ .../s2n_stuffer_read_uint16_hex/Makefile | 39 ++++++++++ .../cbmc-proof.txt | 1 + .../s2n_stuffer_read_uint16_hex_harness.c | 44 +++++++++++ .../s2n_stuffer_read_uint8_hex/Makefile | 39 ++++++++++ .../s2n_stuffer_read_uint8_hex/cbmc-proof.txt | 1 + .../s2n_stuffer_read_uint8_hex_harness.c | 44 +++++++++++ .../proofs/s2n_stuffer_write_hex/Makefile | 40 ++++++++++ .../s2n_stuffer_write_hex/cbmc-proof.txt | 1 + .../s2n_stuffer_write_hex_harness.c | 74 +++++++++++++++++++ .../s2n_stuffer_write_uint16_hex/Makefile | 37 ++++++++++ .../cbmc-proof.txt | 1 + .../s2n_stuffer_write_uint16_hex_harness.c | 63 ++++++++++++++++ .../s2n_stuffer_write_uint8_hex/Makefile | 37 ++++++++++ .../cbmc-proof.txt | 1 + .../s2n_stuffer_write_uint8_hex_harness.c | 63 ++++++++++++++++ 18 files changed, 591 insertions(+) create mode 100644 tests/cbmc/proofs/s2n_stuffer_read_hex/Makefile create mode 100644 tests/cbmc/proofs/s2n_stuffer_read_hex/cbmc-proof.txt create mode 100644 tests/cbmc/proofs/s2n_stuffer_read_hex/s2n_stuffer_read_hex_harness.c create mode 100644 tests/cbmc/proofs/s2n_stuffer_read_uint16_hex/Makefile create mode 100644 tests/cbmc/proofs/s2n_stuffer_read_uint16_hex/cbmc-proof.txt create mode 100644 tests/cbmc/proofs/s2n_stuffer_read_uint16_hex/s2n_stuffer_read_uint16_hex_harness.c create mode 100644 tests/cbmc/proofs/s2n_stuffer_read_uint8_hex/Makefile create mode 100644 tests/cbmc/proofs/s2n_stuffer_read_uint8_hex/cbmc-proof.txt create mode 100644 tests/cbmc/proofs/s2n_stuffer_read_uint8_hex/s2n_stuffer_read_uint8_hex_harness.c create mode 100644 tests/cbmc/proofs/s2n_stuffer_write_hex/Makefile create mode 100644 tests/cbmc/proofs/s2n_stuffer_write_hex/cbmc-proof.txt create mode 100644 tests/cbmc/proofs/s2n_stuffer_write_hex/s2n_stuffer_write_hex_harness.c create mode 100644 tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/Makefile create mode 100644 tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/cbmc-proof.txt create mode 100644 tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/s2n_stuffer_write_uint16_hex_harness.c create mode 100644 tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/Makefile create mode 100644 tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/cbmc-proof.txt create mode 100644 tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/s2n_stuffer_write_uint8_hex_harness.c diff --git a/tests/cbmc/proofs/s2n_stuffer_read_hex/Makefile b/tests/cbmc/proofs/s2n_stuffer_read_hex/Makefile new file mode 100644 index 00000000000..527e2146b25 --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_read_hex/Makefile @@ -0,0 +1,40 @@ +# Copyright 2020 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. + +########### +#Use the default set of CBMC flags +CHECKFLAGS += + +MAX_BLOB_SIZE = 10 +DEFINES += -DMAX_BLOB_SIZE=$(MAX_BLOB_SIZE) + +PROOF_UID = s2n_stuffer_read_hex +HARNESS_ENTRY = $(PROOF_UID)_harness +HARNESS_FILE = $(HARNESS_ENTRY).c + +PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) +PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c +PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c + +PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_hex.c +PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_ensure.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c + +UNWINDSET += s2n_stuffer_read_hex.8:$(MAX_BLOB_SIZE) + +include ../Makefile.common diff --git a/tests/cbmc/proofs/s2n_stuffer_read_hex/cbmc-proof.txt b/tests/cbmc/proofs/s2n_stuffer_read_hex/cbmc-proof.txt new file mode 100644 index 00000000000..6ed46f1258c --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_read_hex/cbmc-proof.txt @@ -0,0 +1 @@ +# This file marks this directory as containing a CBMC proof. diff --git a/tests/cbmc/proofs/s2n_stuffer_read_hex/s2n_stuffer_read_hex_harness.c b/tests/cbmc/proofs/s2n_stuffer_read_hex/s2n_stuffer_read_hex_harness.c new file mode 100644 index 00000000000..4051edc30cf --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_read_hex/s2n_stuffer_read_hex_harness.c @@ -0,0 +1,65 @@ +/* + * Copyright 2020 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 +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" + +void s2n_stuffer_read_hex_harness() +{ + nondet_s2n_mem_init(); + + struct s2n_stuffer *output = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(output))); + + struct s2n_blob *hex_in = cbmc_allocate_s2n_blob(); + __CPROVER_assume(s2n_result_is_ok(s2n_blob_validate(hex_in))); + __CPROVER_assume(s2n_blob_is_bounded(hex_in, MAX_BLOB_SIZE)); + + struct s2n_stuffer old_output = *output; + struct store_byte_from_buffer output_saved_byte = { 0 }; + save_byte_from_blob(&output->blob, &output_saved_byte); + __CPROVER_assume(output_saved_byte.idx < output->write_cursor); + + struct s2n_blob old_hex_in = *hex_in; + struct store_byte_from_buffer old_hex_in_byte = { 0 }; + save_byte_from_blob(hex_in, &old_hex_in_byte); + + s2n_result result = s2n_stuffer_read_hex(output, hex_in); + + struct s2n_stuffer expected_bytes_out = old_output; + struct s2n_blob expected_hex_in = old_hex_in; + + /* On success, the byte equivalent of the hex was written to the stuffer */ + if (s2n_result_is_ok(result)) { + expected_bytes_out.write_cursor += old_hex_in.size / 2; + expected_bytes_out.high_water_mark = MAX(expected_bytes_out.write_cursor, + old_output.high_water_mark); + } + + /* Memory may be allocated on either success or failure, + * because we allocated the memory before we start writing. */ + if (output->blob.size > old_output.blob.size) { + expected_bytes_out.blob = output->blob; + } + + assert(s2n_result_is_ok(s2n_stuffer_validate(output))); + assert_stuffer_equivalence(output, &expected_bytes_out, &output_saved_byte); + assert(s2n_result_is_ok(s2n_blob_validate(hex_in))); + assert_blob_equivalence(hex_in, &expected_hex_in, &old_hex_in_byte); +} diff --git a/tests/cbmc/proofs/s2n_stuffer_read_uint16_hex/Makefile b/tests/cbmc/proofs/s2n_stuffer_read_uint16_hex/Makefile new file mode 100644 index 00000000000..1952ae9eb72 --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_read_uint16_hex/Makefile @@ -0,0 +1,39 @@ +# Copyright 2020 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. + +########### +#Use the default set of CBMC flags +CHECKFLAGS += + +PROOF_UID = s2n_stuffer_read_uint16_hex +HARNESS_ENTRY = $(PROOF_UID)_harness +HARNESS_FILE = $(HARNESS_ENTRY).c + +PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) +PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c +PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c + +PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_hex.c +PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_ensure.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c + +REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_mem_c_s2n_mem_cleanup_impl + +UNWINDSET += __CPROVER_file_local_s2n_stuffer_hex_c_s2n_stuffer_hex_read_n_bytes.14:5 + +include ../Makefile.common diff --git a/tests/cbmc/proofs/s2n_stuffer_read_uint16_hex/cbmc-proof.txt b/tests/cbmc/proofs/s2n_stuffer_read_uint16_hex/cbmc-proof.txt new file mode 100644 index 00000000000..6ed46f1258c --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_read_uint16_hex/cbmc-proof.txt @@ -0,0 +1 @@ +# This file marks this directory as containing a CBMC proof. diff --git a/tests/cbmc/proofs/s2n_stuffer_read_uint16_hex/s2n_stuffer_read_uint16_hex_harness.c b/tests/cbmc/proofs/s2n_stuffer_read_uint16_hex/s2n_stuffer_read_uint16_hex_harness.c new file mode 100644 index 00000000000..97211bbd47e --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_read_uint16_hex/s2n_stuffer_read_uint16_hex_harness.c @@ -0,0 +1,44 @@ +/* + * Copyright 2020 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 +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" + +void s2n_stuffer_read_uint16_hex_harness() +{ + nondet_s2n_mem_init(); + + struct s2n_stuffer *hex_in = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(hex_in))); + + struct s2n_stuffer old_hex_in = *hex_in; + struct store_byte_from_buffer old_byte = { 0 }; + save_byte_from_blob(&hex_in->blob, &old_byte); + + uint16_t out; + s2n_stuffer_read_uint16_hex(hex_in, &out); + + size_t expected_read = 4; + struct s2n_stuffer expected_hex_in = old_hex_in; + if (expected_hex_in.write_cursor >= expected_hex_in.read_cursor + expected_read) { + expected_hex_in.read_cursor += expected_read; + } + assert(s2n_result_is_ok(s2n_stuffer_validate(hex_in))); + assert_stuffer_equivalence(hex_in, &expected_hex_in, &old_byte); +} diff --git a/tests/cbmc/proofs/s2n_stuffer_read_uint8_hex/Makefile b/tests/cbmc/proofs/s2n_stuffer_read_uint8_hex/Makefile new file mode 100644 index 00000000000..6c05e6c3fdd --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_read_uint8_hex/Makefile @@ -0,0 +1,39 @@ +# Copyright 2020 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. + +########### +#Use the default set of CBMC flags +CHECKFLAGS += + +PROOF_UID = s2n_stuffer_read_uint8_hex +HARNESS_ENTRY = $(PROOF_UID)_harness +HARNESS_FILE = $(HARNESS_ENTRY).c + +PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) +PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c +PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c + +PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_hex.c +PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_ensure.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c + +REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_mem_c_s2n_mem_cleanup_impl + +UNWINDSET += __CPROVER_file_local_s2n_stuffer_hex_c_s2n_stuffer_hex_read_n_bytes.14:3 + +include ../Makefile.common diff --git a/tests/cbmc/proofs/s2n_stuffer_read_uint8_hex/cbmc-proof.txt b/tests/cbmc/proofs/s2n_stuffer_read_uint8_hex/cbmc-proof.txt new file mode 100644 index 00000000000..6ed46f1258c --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_read_uint8_hex/cbmc-proof.txt @@ -0,0 +1 @@ +# This file marks this directory as containing a CBMC proof. diff --git a/tests/cbmc/proofs/s2n_stuffer_read_uint8_hex/s2n_stuffer_read_uint8_hex_harness.c b/tests/cbmc/proofs/s2n_stuffer_read_uint8_hex/s2n_stuffer_read_uint8_hex_harness.c new file mode 100644 index 00000000000..0738499a4f3 --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_read_uint8_hex/s2n_stuffer_read_uint8_hex_harness.c @@ -0,0 +1,44 @@ +/* + * Copyright 2020 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 +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" + +void s2n_stuffer_read_uint8_hex_harness() +{ + nondet_s2n_mem_init(); + + struct s2n_stuffer *hex_in = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(hex_in))); + + struct s2n_stuffer old_hex_in = *hex_in; + struct store_byte_from_buffer old_byte = { 0 }; + save_byte_from_blob(&hex_in->blob, &old_byte); + + uint8_t out; + s2n_stuffer_read_uint8_hex(hex_in, &out); + + size_t expected_read = 2; + struct s2n_stuffer expected_hex_in = old_hex_in; + if (expected_hex_in.write_cursor >= expected_hex_in.read_cursor + expected_read) { + expected_hex_in.read_cursor += expected_read; + } + assert(s2n_result_is_ok(s2n_stuffer_validate(hex_in))); + assert_stuffer_equivalence(hex_in, &expected_hex_in, &old_byte); +} diff --git a/tests/cbmc/proofs/s2n_stuffer_write_hex/Makefile b/tests/cbmc/proofs/s2n_stuffer_write_hex/Makefile new file mode 100644 index 00000000000..a09b7f8a6e8 --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_write_hex/Makefile @@ -0,0 +1,40 @@ +# Copyright 2020 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. + +########### +#Use the default set of CBMC flags +CHECKFLAGS += + +MAX_BLOB_SIZE = 10 +DEFINES += -DMAX_BLOB_SIZE=$(MAX_BLOB_SIZE) + +PROOF_UID = s2n_stuffer_write_hex +HARNESS_ENTRY = $(PROOF_UID)_harness +HARNESS_FILE = $(HARNESS_ENTRY).c + +PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) +PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c +PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c + +PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_hex.c +PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_ensure.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c + +UNWINDSET += s2n_stuffer_write_hex.3:$(call addone,$(MAX_BLOB_SIZE)) + +include ../Makefile.common diff --git a/tests/cbmc/proofs/s2n_stuffer_write_hex/cbmc-proof.txt b/tests/cbmc/proofs/s2n_stuffer_write_hex/cbmc-proof.txt new file mode 100644 index 00000000000..6ed46f1258c --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_write_hex/cbmc-proof.txt @@ -0,0 +1 @@ +# This file marks this directory as containing a CBMC proof. diff --git a/tests/cbmc/proofs/s2n_stuffer_write_hex/s2n_stuffer_write_hex_harness.c b/tests/cbmc/proofs/s2n_stuffer_write_hex/s2n_stuffer_write_hex_harness.c new file mode 100644 index 00000000000..6b5bdc20a1c --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_write_hex/s2n_stuffer_write_hex_harness.c @@ -0,0 +1,74 @@ +/* + * Copyright 2020 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 +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" + +void s2n_stuffer_write_hex_harness() +{ + nondet_s2n_mem_init(); + + struct s2n_stuffer *hex_out = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(hex_out))); + + struct s2n_blob *bytes_in = cbmc_allocate_s2n_blob(); + __CPROVER_assume(s2n_result_is_ok(s2n_blob_validate(bytes_in))); + __CPROVER_assume(s2n_blob_is_bounded(bytes_in, MAX_BLOB_SIZE - 1)); + + size_t expected_written = bytes_in->size * 2; + size_t test_offset = nondet_size_t(); + __CPROVER_assume(0 <= test_offset); + __CPROVER_assume(test_offset < expected_written); + + struct s2n_stuffer old_hex_out = *hex_out; + struct store_byte_from_buffer old_hex_out_byte = { 0 }; + save_byte_from_blob(&hex_out->blob, &old_hex_out_byte); + __CPROVER_assume(old_hex_out_byte.idx < hex_out->write_cursor); + + struct s2n_blob old_bytes_in = *bytes_in; + struct store_byte_from_buffer old_bytes_in_byte = { 0 }; + save_byte_from_blob(bytes_in, &old_bytes_in_byte); + + s2n_result result = s2n_stuffer_write_hex(hex_out, bytes_in); + + struct s2n_stuffer expected_hex_out = old_hex_out; + struct s2n_blob expected_bytes_in = old_bytes_in; + + if (s2n_result_is_ok(result)) { + /* On success, the hex equivalent of the bytes is written to the stuffer */ + expected_hex_out.write_cursor += expected_written; + expected_hex_out.high_water_mark = MAX(expected_hex_out.write_cursor, + old_hex_out.high_water_mark); + + /* Any new bytes written should match the expected hex pattern */ + uint8_t c = hex_out->blob.data[old_hex_out.write_cursor + test_offset]; + assert(('0' <= c && c <= '9') || ('a' <= c && c <= 'f')); + } + + /* Memory may be allocated on either success or failure, + * because we allocated the memory before we start writing. */ + if (hex_out->blob.size > old_hex_out.blob.size) { + expected_hex_out.blob = hex_out->blob; + } + + assert(s2n_result_is_ok(s2n_stuffer_validate(hex_out))); + assert_stuffer_equivalence(hex_out, &expected_hex_out, &old_hex_out_byte); + assert(s2n_result_is_ok(s2n_blob_validate(bytes_in))); + assert_blob_equivalence(bytes_in, &expected_bytes_in, &old_bytes_in_byte); +} diff --git a/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/Makefile b/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/Makefile new file mode 100644 index 00000000000..255787c81b9 --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/Makefile @@ -0,0 +1,37 @@ +# Copyright 2020 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. + +########### +#Use the default set of CBMC flags +CHECKFLAGS += + +PROOF_UID = s2n_stuffer_write_uint16_hex +HARNESS_ENTRY = $(PROOF_UID)_harness +HARNESS_FILE = $(HARNESS_ENTRY).c + +PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) +PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c +PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c + +PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_hex.c +PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_ensure.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c + +UNWINDSET += __CPROVER_file_local_s2n_stuffer_hex_c_s2n_stuffer_hex_write_n_bytes.4:5 + +include ../Makefile.common diff --git a/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/cbmc-proof.txt b/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/cbmc-proof.txt new file mode 100644 index 00000000000..6ed46f1258c --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/cbmc-proof.txt @@ -0,0 +1 @@ +# This file marks this directory as containing a CBMC proof. diff --git a/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/s2n_stuffer_write_uint16_hex_harness.c b/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/s2n_stuffer_write_uint16_hex_harness.c new file mode 100644 index 00000000000..cf40ee82fae --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/s2n_stuffer_write_uint16_hex_harness.c @@ -0,0 +1,63 @@ +/* + * Copyright 2020 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 +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" + +void s2n_stuffer_write_uint16_hex_harness() +{ + nondet_s2n_mem_init(); + + struct s2n_stuffer *hex_out = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(hex_out))); + + struct s2n_stuffer old_hex_out = *hex_out; + struct store_byte_from_buffer old_hex_out_byte = { 0 }; + save_byte_from_blob(&hex_out->blob, &old_hex_out_byte); + __CPROVER_assume(old_hex_out_byte.idx < hex_out->write_cursor); + + uint16_t byte_in = nondet_uint16_t(); + s2n_result result = s2n_stuffer_write_uint16_hex(hex_out, byte_in); + + struct s2n_stuffer expected_hex_out = old_hex_out; + size_t expected_written = 4; + size_t test_offset = nondet_size_t(); + __CPROVER_assume(0 <= test_offset); + __CPROVER_assume(test_offset < expected_written); + + if (s2n_result_is_ok(result)) { + /* On success, the hex equivalent of the bytes is written to the stuffer */ + expected_hex_out.write_cursor += expected_written; + expected_hex_out.high_water_mark = MAX(expected_hex_out.write_cursor, + old_hex_out.high_water_mark); + + /* New bytes written should match the expected hex pattern */ + uint8_t c = hex_out->blob.data[old_hex_out.write_cursor + test_offset]; + assert(('0' <= c && c <= '9') || ('a' <= c && c <= 'f')); + } + + /* Memory may be allocated on either success or failure, + * because we allocated the memory before we start writing. */ + if (hex_out->blob.size > old_hex_out.blob.size) { + expected_hex_out.blob = hex_out->blob; + } + + assert(s2n_result_is_ok(s2n_stuffer_validate(hex_out))); + assert_stuffer_equivalence(hex_out, &expected_hex_out, &old_hex_out_byte); +} diff --git a/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/Makefile b/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/Makefile new file mode 100644 index 00000000000..6a5695128fc --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/Makefile @@ -0,0 +1,37 @@ +# Copyright 2020 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. + +########### +#Use the default set of CBMC flags +CHECKFLAGS += + +PROOF_UID = s2n_stuffer_write_uint8_hex +HARNESS_ENTRY = $(PROOF_UID)_harness +HARNESS_FILE = $(HARNESS_ENTRY).c + +PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) +PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c +PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c + +PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_hex.c +PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_ensure.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c + +UNWINDSET += __CPROVER_file_local_s2n_stuffer_hex_c_s2n_stuffer_hex_write_n_bytes.4:3 + +include ../Makefile.common diff --git a/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/cbmc-proof.txt b/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/cbmc-proof.txt new file mode 100644 index 00000000000..6ed46f1258c --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/cbmc-proof.txt @@ -0,0 +1 @@ +# This file marks this directory as containing a CBMC proof. diff --git a/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/s2n_stuffer_write_uint8_hex_harness.c b/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/s2n_stuffer_write_uint8_hex_harness.c new file mode 100644 index 00000000000..6d5372cea7c --- /dev/null +++ b/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/s2n_stuffer_write_uint8_hex_harness.c @@ -0,0 +1,63 @@ +/* + * Copyright 2020 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 +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" + +void s2n_stuffer_write_uint8_hex_harness() +{ + nondet_s2n_mem_init(); + + struct s2n_stuffer *hex_out = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(hex_out))); + + struct s2n_stuffer old_hex_out = *hex_out; + struct store_byte_from_buffer old_hex_out_byte = { 0 }; + save_byte_from_blob(&hex_out->blob, &old_hex_out_byte); + __CPROVER_assume(old_hex_out_byte.idx < hex_out->write_cursor); + + uint8_t byte_in = nondet_uint8_t(); + s2n_result result = s2n_stuffer_write_uint8_hex(hex_out, byte_in); + + struct s2n_stuffer expected_hex_out = old_hex_out; + size_t expected_written = 2; + size_t test_offset = nondet_size_t(); + __CPROVER_assume(0 <= test_offset); + __CPROVER_assume(test_offset < expected_written); + + if (s2n_result_is_ok(result)) { + /* On success, the hex equivalent of the bytes is written to the stuffer */ + expected_hex_out.write_cursor += expected_written; + expected_hex_out.high_water_mark = MAX(expected_hex_out.write_cursor, + old_hex_out.high_water_mark); + + /* New bytes written should match the expected hex pattern */ + uint8_t c = hex_out->blob.data[old_hex_out.write_cursor + test_offset]; + assert(('0' <= c && c <= '9') || ('a' <= c && c <= 'f')); + } + + /* Memory may be allocated on either success or failure, + * because we allocated the memory before we start writing. */ + if (hex_out->blob.size > old_hex_out.blob.size) { + expected_hex_out.blob = hex_out->blob; + } + + assert(s2n_result_is_ok(s2n_stuffer_validate(hex_out))); + assert_stuffer_equivalence(hex_out, &expected_hex_out, &old_hex_out_byte); +} From b9a97bcf3dc32697b33adffec80b2f8416b6165a Mon Sep 17 00:00:00 2001 From: Roderick Chapman Date: Fri, 2 Aug 2024 17:58:05 +0100 Subject: [PATCH 04/19] Adopt CBMC 6.1 and cbmc-viewer 3.9 (#4661) Signed-off-by: Rod Chapman Signed-off-by: Ubuntu Co-authored-by: Lindsay Stewart --- .github/workflows/proof_ci.yaml | 57 +++++++++++++++++-- .../workflows/proof_ci_resources/config.yaml | 4 +- tests/cbmc/proofs/Makefile-project-defines | 1 - tests/cbmc/proofs/Makefile.common | 7 ++- tests/cbmc/proofs/s2n_array_new/Makefile | 1 + .../Makefile | 1 + .../Makefile | 2 + .../s2n_dh_generate_ephemeral_key/Makefile | 1 + .../s2n_dh_p_g_Ys_to_dh_params/Makefile | 1 + .../cbmc/proofs/s2n_dh_params_check/Makefile | 2 + tests/cbmc/proofs/s2n_dh_params_copy/Makefile | 1 + tests/cbmc/proofs/s2n_dh_params_free/Makefile | 1 + tests/cbmc/proofs/s2n_free/Makefile | 1 + tests/cbmc/proofs/s2n_free_object/Makefile | 1 + tests/cbmc/proofs/s2n_free_or_wipe/Makefile | 1 + tests/cbmc/proofs/s2n_hash_free/Makefile | 1 - tests/cbmc/proofs/s2n_hash_update/Makefile | 1 - tests/cbmc/proofs/s2n_hmac_free/Makefile | 3 +- .../s2n_hmac_free/s2n_hmac_free_harness.c | 4 +- tests/cbmc/proofs/s2n_hmac_init/Makefile | 10 ++-- tests/cbmc/proofs/s2n_hmac_update/Makefile | 1 - tests/cbmc/proofs/s2n_mem_cleanup/Makefile | 3 +- tests/cbmc/proofs/s2n_mem_init/Makefile | 1 + tests/cbmc/proofs/s2n_stuffer_alloc/Makefile | 1 + .../s2n_stuffer_alloc_ro_from_string/Makefile | 2 + tests/cbmc/proofs/s2n_stuffer_copy/Makefile | 1 + .../proofs/s2n_stuffer_extract_blob/Makefile | 4 +- tests/cbmc/proofs/s2n_stuffer_free/Makefile | 1 + .../s2n_stuffer_growable_alloc/Makefile | 1 + .../s2n_stuffer_printf_harness.c | 5 ++ .../s2n_stuffer_private_key_from_pem/Makefile | 2 +- .../proofs/s2n_stuffer_raw_write/Makefile | 3 + .../proofs/s2n_stuffer_read_base64/Makefile | 1 + .../cbmc/proofs/s2n_stuffer_read_hex/Makefile | 3 + .../proofs/s2n_stuffer_read_line/Makefile | 1 + .../proofs/s2n_stuffer_read_token/Makefile | 2 + .../proofs/s2n_stuffer_recv_from_fd/Makefile | 1 + .../cbmc/proofs/s2n_stuffer_reserve/Makefile | 1 + .../proofs/s2n_stuffer_reserve_space/Makefile | 3 + .../s2n_stuffer_reserve_uint16/Makefile | 2 + .../s2n_stuffer_reserve_uint24/Makefile | 2 + tests/cbmc/proofs/s2n_stuffer_resize/Makefile | 1 + .../s2n_stuffer_resize_if_empty/Makefile | 2 + .../proofs/s2n_stuffer_skip_write/Makefile | 1 + tests/cbmc/proofs/s2n_stuffer_write/Makefile | 3 + .../proofs/s2n_stuffer_write_base64/Makefile | 1 + .../proofs/s2n_stuffer_write_bytes/Makefile | 3 + .../proofs/s2n_stuffer_write_hex/Makefile | 3 + .../s2n_stuffer_write_network_order/Makefile | 2 + .../proofs/s2n_stuffer_write_uint16/Makefile | 3 + .../s2n_stuffer_write_uint16_hex/Makefile | 3 + .../proofs/s2n_stuffer_write_uint24/Makefile | 3 + .../proofs/s2n_stuffer_write_uint32/Makefile | 3 + .../proofs/s2n_stuffer_write_uint64/Makefile | 3 + .../proofs/s2n_stuffer_write_uint8/Makefile | 3 + .../s2n_stuffer_write_uint8_hex/Makefile | 3 + tests/cbmc/sources/cbmc_utils.c | 19 ++++--- .../cbmc/stubs/darwin_check_fd_set_overflow.c | 25 ++++++++ tests/cbmc/stubs/s2n_is_in_fips_mode.c | 6 +- utils/s2n_ensure.h | 4 ++ 60 files changed, 197 insertions(+), 36 deletions(-) create mode 100644 tests/cbmc/stubs/darwin_check_fd_set_overflow.c diff --git a/.github/workflows/proof_ci.yaml b/.github/workflows/proof_ci.yaml index f8596b83028..351c906da35 100644 --- a/.github/workflows/proof_ci.yaml +++ b/.github/workflows/proof_ci.yaml @@ -1,6 +1,6 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 -# CBMC starter kit 2.9 +# CBMC starter kit 2.10 name: Run CBMC proofs on: push: @@ -38,11 +38,11 @@ jobs: - name: Parse config file run: | CONFIG_FILE='.github/workflows/proof_ci_resources/config.yaml' - for setting in cadical-tag cbmc-version cbmc-viewer-version kissat-tag litani-version proofs-dir run-cbmc-proofs-command; do + for setting in cadical-tag cbmc-version cbmc-viewer-version kissat-tag litani-version z3-version bitwuzla-version proofs-dir run-cbmc-proofs-command; do VAR=$(echo $setting | tr "[:lower:]" "[:upper:]" | tr - _) echo "${VAR}"=$(yq .$setting $CONFIG_FILE) >> $GITHUB_ENV done - - name: Ensure CBMC, CBMC viewer, Litani versions have been specified + - name: Ensure CBMC, CBMC viewer, Litani, Z3, Bitwuzla versions have been specified shell: bash run: | should_exit=false @@ -58,6 +58,22 @@ jobs: echo "You must specify a Litani version (e.g. 'latest' or '1.27.0')" should_exit=true fi + if [ "${{ env.Z3_VERSION }}" == "" ]; then + echo "You must specify a Z3 version (e.g. '4.13.0')" + should_exit=true + fi + if [ "${{ env.Z3_VERSION }}" == "latest" ]; then + echo "Z3 latest not supported at this time. You must specify an exact Z3 version (e.g. '4.13.0')" + should_exit=true + fi + if [ "${{ env.BITWUZLA_VERSION }}" == "" ]; then + echo "You must specify a Bitwuzla version (e.g. '0.5.0')" + should_exit=true + fi + if [ "${{ env.BITWUZLA_VERSION }}" == "latest" ]; then + echo "Bitwuzla latest not supported at this time. You must specify an exact version (e.g. '0.5.0')" + should_exit=true + fi if [[ "$should_exit" == true ]]; then exit 1; fi - name: Install latest CBMC if: ${{ env.CBMC_VERSION == 'latest' }} @@ -84,7 +100,7 @@ jobs: run: | CBMC_VIEWER_REL="https://api.github.com/repos/model-checking/cbmc-viewer/releases/latest" CBMC_VIEWER_VERSION=$(curl -s $CBMC_VIEWER_REL --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' | jq -r .name | sed 's/viewer-//') - pip3 install cbmc-viewer==$CBMC_VIEWER_VERSION + sudo pip3 install cbmc-viewer==$CBMC_VIEWER_VERSION - name: Install CBMC viewer ${{ env.CBMC_VIEWER_VERSION }} if: ${{ env.CBMC_VIEWER_VERSION != 'latest' }} shell: bash @@ -92,7 +108,7 @@ jobs: sudo apt-get update sudo apt-get install --no-install-recommends --yes \ build-essential universal-ctags - pip3 install cbmc-viewer==${{ env.CBMC_VIEWER_VERSION }} + sudo pip3 install cbmc-viewer==${{ env.CBMC_VIEWER_VERSION }} - name: Install latest Litani if: ${{ env.LITANI_VERSION == 'latest' }} shell: bash @@ -114,6 +130,37 @@ jobs: sudo apt-get update sudo apt-get install --no-install-recommends --yes ./litani.deb rm ./litani.deb + - name: Install Z3 ${{ env.Z3_VERSION }} + if: ${{ env.Z3_VERSION != 'latest' }} + shell: bash + run: | + curl -o z3.zip -L \ + https://github.com/Z3Prover/z3/releases/download/z3-${{ env.Z3_VERSION }}/z3-${{ env.Z3_VERSION }}-x64-glibc-2.31.zip + sudo apt-get install --no-install-recommends --yes unzip + unzip z3.zip + cd z3-${{ env.Z3_VERSION }}-x64-glibc-2.31/bin \ + && echo "Adding $(pwd) to PATH for Z3" \ + && echo "$(pwd)" >> $GITHUB_PATH + rm ../../z3.zip + - name: Build and Install Bitwuzla ${{ env.BITWUZLA_VERSION }} + if: ${{ env.BITWUZLA_VERSION != 'latest' }} + shell: bash + run: | + echo "Installing Bitwuzla dependencies" + sudo apt-get update + sudo apt-get install --no-install-recommends --yes libgmp-dev cmake + sudo pip3 install meson + echo "Building Bitwuzla" + BITWUZLA_TAG_NAME=${{ env.BITWUZLA_VERSION }} + git clone https://github.com/bitwuzla/bitwuzla.git \ + && cd bitwuzla \ + && git checkout $BITWUZLA_TAG_NAME \ + && ./configure.py \ + && cd build \ + && ninja; + cd src/main \ + && echo "Adding $(pwd) to PATH for Bitwuzla" \ + && echo "$(pwd)" >> $GITHUB_PATH - name: Install ${{ env.KISSAT_TAG }} kissat if: ${{ env.KISSAT_TAG != '' }} shell: bash diff --git a/.github/workflows/proof_ci_resources/config.yaml b/.github/workflows/proof_ci_resources/config.yaml index 0356dd1f288..6c44d965a6b 100644 --- a/.github/workflows/proof_ci_resources/config.yaml +++ b/.github/workflows/proof_ci_resources/config.yaml @@ -1,7 +1,9 @@ cadical-tag: latest -cbmc-version: "5.95.1" +cbmc-version: "6.1.0" cbmc-viewer-version: latest kissat-tag: latest litani-version: latest +z3-version: "4.13.0" +bitwuzla-version: "0.5.0" proofs-dir: tests/cbmc/proofs run-cbmc-proofs-command: ./run-cbmc-proofs.py diff --git a/tests/cbmc/proofs/Makefile-project-defines b/tests/cbmc/proofs/Makefile-project-defines index 281dba0ffb6..d0e92588782 100644 --- a/tests/cbmc/proofs/Makefile-project-defines +++ b/tests/cbmc/proofs/Makefile-project-defines @@ -46,7 +46,6 @@ CHECKFLAGS += --pointer-primitive-check # We override abort() to be assert(0) PROOF_SOURCES += $(PROOF_STUB)/abort_override_assert_false.c -REMOVE_FUNCTION_BODY += abort # Useful for setting unwind limits to one more than some value. addone = $(shell echo $$(( $(1) + 1))) diff --git a/tests/cbmc/proofs/Makefile.common b/tests/cbmc/proofs/Makefile.common index 43a3cab6aa6..f4faa231c26 100644 --- a/tests/cbmc/proofs/Makefile.common +++ b/tests/cbmc/proofs/Makefile.common @@ -259,6 +259,11 @@ CBMC_FLAG_FLUSH ?= --flush CBMCFLAGS += $(CBMC_FLAG_FLUSH) +# CBMC 6.0.0 enables all standard checks by default, which can make coverage analysis +# very slow. See https://github.com/diffblue/cbmc/issues/8389 +# For now, we disable these checks when generating coverage info. +COVERFLAGS ?= --no-standard-checks + # CBMC flags used for property checking CHECKFLAGS += $(CBMC_FLAG_BOUNDS_CHECK) @@ -382,6 +387,7 @@ PROJECT_SOURCES ?= # PROOF_SOURCES is the list of proof source files to compile, including # the proof harness, and including any function stubs being used. PROOF_SOURCES ?= +PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c # The number of seconds that CBMC should be allowed to run for before # being forcefully terminated. Currently, this is set to be less than @@ -492,7 +498,6 @@ COMMA :=, # Set C compiler defines CBMCFLAGS += --object-bits $(CBMC_OBJECT_BITS) -COMPILE_FLAGS += --object-bits $(CBMC_OBJECT_BITS) DEFINES += -DCBMC=1 DEFINES += -DCBMC_OBJECT_BITS=$(CBMC_OBJECT_BITS) diff --git a/tests/cbmc/proofs/s2n_array_new/Makefile b/tests/cbmc/proofs/s2n_array_new/Makefile index 6cd64107a73..87a2c94ac76 100644 --- a/tests/cbmc/proofs/s2n_array_new/Makefile +++ b/tests/cbmc/proofs/s2n_array_new/Makefile @@ -25,6 +25,7 @@ PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/memset_havoc.c PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c diff --git a/tests/cbmc/proofs/s2n_dh_compute_shared_secret_as_client/Makefile b/tests/cbmc/proofs/s2n_dh_compute_shared_secret_as_client/Makefile index cb7a239897c..39833089310 100644 --- a/tests/cbmc/proofs/s2n_dh_compute_shared_secret_as_client/Makefile +++ b/tests/cbmc/proofs/s2n_dh_compute_shared_secret_as_client/Makefile @@ -26,6 +26,7 @@ PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c diff --git a/tests/cbmc/proofs/s2n_dh_compute_shared_secret_as_server/Makefile b/tests/cbmc/proofs/s2n_dh_compute_shared_secret_as_server/Makefile index 518b7eefddf..866f6f441b3 100644 --- a/tests/cbmc/proofs/s2n_dh_compute_shared_secret_as_server/Makefile +++ b/tests/cbmc/proofs/s2n_dh_compute_shared_secret_as_server/Makefile @@ -25,9 +25,11 @@ PROOF_SOURCES += $(OPENSSL_SOURCE)/bn_override.c PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c +PROOF_SOURCES += $(PROOF_STUB)/s2n_ensure.c PROJECT_SOURCES += $(SRCDIR)/crypto/s2n_dhe.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c diff --git a/tests/cbmc/proofs/s2n_dh_generate_ephemeral_key/Makefile b/tests/cbmc/proofs/s2n_dh_generate_ephemeral_key/Makefile index f57f5afb42a..b7f5e6b28c8 100644 --- a/tests/cbmc/proofs/s2n_dh_generate_ephemeral_key/Makefile +++ b/tests/cbmc/proofs/s2n_dh_generate_ephemeral_key/Makefile @@ -29,6 +29,7 @@ PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROJECT_SOURCES += $(SRCDIR)/crypto/s2n_dhe.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c UNWINDSET += diff --git a/tests/cbmc/proofs/s2n_dh_p_g_Ys_to_dh_params/Makefile b/tests/cbmc/proofs/s2n_dh_p_g_Ys_to_dh_params/Makefile index 08f38129a3e..7c3d58165d5 100644 --- a/tests/cbmc/proofs/s2n_dh_p_g_Ys_to_dh_params/Makefile +++ b/tests/cbmc/proofs/s2n_dh_p_g_Ys_to_dh_params/Makefile @@ -31,6 +31,7 @@ PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c PROJECT_SOURCES += $(SRCDIR)/crypto/s2n_dhe.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c UNWINDSET += diff --git a/tests/cbmc/proofs/s2n_dh_params_check/Makefile b/tests/cbmc/proofs/s2n_dh_params_check/Makefile index a1e4b14871d..334ae42c373 100644 --- a/tests/cbmc/proofs/s2n_dh_params_check/Makefile +++ b/tests/cbmc/proofs/s2n_dh_params_check/Makefile @@ -21,6 +21,7 @@ HARNESS_FILE = $(HARNESS_ENTRY).c PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(OPENSSL_SOURCE)/dh_override.c +PROOF_SOURCES += $(OPENSSL_SOURCE)/bn_override.c PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c @@ -28,6 +29,7 @@ PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROJECT_SOURCES += $(SRCDIR)/crypto/s2n_dhe.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c UNWINDSET += diff --git a/tests/cbmc/proofs/s2n_dh_params_copy/Makefile b/tests/cbmc/proofs/s2n_dh_params_copy/Makefile index 9958084d620..9db65a488a9 100644 --- a/tests/cbmc/proofs/s2n_dh_params_copy/Makefile +++ b/tests/cbmc/proofs/s2n_dh_params_copy/Makefile @@ -29,6 +29,7 @@ PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROJECT_SOURCES += $(SRCDIR)/crypto/s2n_dhe.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c UNWINDSET += diff --git a/tests/cbmc/proofs/s2n_dh_params_free/Makefile b/tests/cbmc/proofs/s2n_dh_params_free/Makefile index a9a497874de..23bc9c5499e 100644 --- a/tests/cbmc/proofs/s2n_dh_params_free/Makefile +++ b/tests/cbmc/proofs/s2n_dh_params_free/Makefile @@ -29,6 +29,7 @@ PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROJECT_SOURCES += $(SRCDIR)/crypto/s2n_dhe.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c UNWINDSET += diff --git a/tests/cbmc/proofs/s2n_free/Makefile b/tests/cbmc/proofs/s2n_free/Makefile index c3812b54e4c..0d11a263432 100644 --- a/tests/cbmc/proofs/s2n_free/Makefile +++ b/tests/cbmc/proofs/s2n_free/Makefile @@ -29,6 +29,7 @@ PROOF_SOURCES += $(PROOF_STUB)/sysconf.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_mem_c_s2n_mem_cleanup_impl diff --git a/tests/cbmc/proofs/s2n_free_object/Makefile b/tests/cbmc/proofs/s2n_free_object/Makefile index 8e7bc5d0ac6..4517211c1d1 100644 --- a/tests/cbmc/proofs/s2n_free_object/Makefile +++ b/tests/cbmc/proofs/s2n_free_object/Makefile @@ -28,6 +28,7 @@ PROOF_SOURCES += $(PROOF_STUB)/sysconf.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_mem_c_s2n_mem_cleanup_impl diff --git a/tests/cbmc/proofs/s2n_free_or_wipe/Makefile b/tests/cbmc/proofs/s2n_free_or_wipe/Makefile index 4f30e96ef79..3cd080754c1 100644 --- a/tests/cbmc/proofs/s2n_free_or_wipe/Makefile +++ b/tests/cbmc/proofs/s2n_free_or_wipe/Makefile @@ -29,6 +29,7 @@ PROOF_SOURCES += $(PROOF_STUB)/sysconf.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_mem_c_s2n_mem_cleanup_impl diff --git a/tests/cbmc/proofs/s2n_hash_free/Makefile b/tests/cbmc/proofs/s2n_hash_free/Makefile index 8bed36899ef..47028287c60 100644 --- a/tests/cbmc/proofs/s2n_hash_free/Makefile +++ b/tests/cbmc/proofs/s2n_hash_free/Makefile @@ -37,7 +37,6 @@ REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_evp_hash_reset REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_low_level_hash_init REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_low_level_hash_new REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_low_level_hash_reset -REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_hash_allow_md5_for_fips REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_evp_hash_allow_md5_for_fips UNWINDSET += diff --git a/tests/cbmc/proofs/s2n_hash_update/Makefile b/tests/cbmc/proofs/s2n_hash_update/Makefile index 4c5a539b6d0..203d16970a7 100644 --- a/tests/cbmc/proofs/s2n_hash_update/Makefile +++ b/tests/cbmc/proofs/s2n_hash_update/Makefile @@ -36,7 +36,6 @@ PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c # We abstract these functions because manual inspection demonstrates they are unreachable. REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_evp_hash_digest -REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_hash_digest_size REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_low_level_hash_digest UNWINDSET += diff --git a/tests/cbmc/proofs/s2n_hmac_free/Makefile b/tests/cbmc/proofs/s2n_hmac_free/Makefile index 2100f42af63..bb614e0ba6c 100644 --- a/tests/cbmc/proofs/s2n_hmac_free/Makefile +++ b/tests/cbmc/proofs/s2n_hmac_free/Makefile @@ -23,6 +23,7 @@ PROOF_SOURCES += $(OPENSSL_SOURCE)/bn_override.c PROOF_SOURCES += $(OPENSSL_SOURCE)/ec_override.c PROOF_SOURCES += $(OPENSSL_SOURCE)/evp_override.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_STUB)/s2n_is_in_fips_mode.c PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) @@ -37,8 +38,6 @@ REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_evp_hash_reset REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_low_level_hash_init REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_low_level_hash_new REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_low_level_hash_reset -REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_hash_allow_md5_for_fips -REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_evp_hash_allow_md5_for_fips UNWINDSET += diff --git a/tests/cbmc/proofs/s2n_hmac_free/s2n_hmac_free_harness.c b/tests/cbmc/proofs/s2n_hmac_free/s2n_hmac_free_harness.c index c10f670b4d3..aeff29282ad 100644 --- a/tests/cbmc/proofs/s2n_hmac_free/s2n_hmac_free_harness.c +++ b/tests/cbmc/proofs/s2n_hmac_free/s2n_hmac_free_harness.c @@ -43,7 +43,7 @@ void s2n_hmac_free_harness() assert(state->inner_just_key.hash_impl->free != NULL); assert(state->outer.hash_impl->free != NULL); assert(state->outer_just_key.hash_impl->free != NULL); - + if (s2n_is_in_fips_mode()) { assert(state->inner.digest.high_level.evp.ctx == NULL); assert(state->inner.digest.high_level.evp_md5_secondary.ctx == NULL); @@ -93,7 +93,7 @@ void s2n_hmac_free_harness() */ free_rc_keys_from_hash_state(&saved_inner_hash_state); free_rc_keys_from_hash_state(&saved_inner_just_key_hash_state); - free_rc_keys_from_hash_state(&saved_inner_hash_state); + free_rc_keys_from_hash_state(&saved_outer_hash_state); free_rc_keys_from_hash_state(&saved_outer_just_key_hash_state); } /* 3. free our heap-allocated `state` since `s2n_hash_free` only `free`s the contents. */ diff --git a/tests/cbmc/proofs/s2n_hmac_init/Makefile b/tests/cbmc/proofs/s2n_hmac_init/Makefile index d08ff2430e1..9538174d3a6 100644 --- a/tests/cbmc/proofs/s2n_hmac_init/Makefile +++ b/tests/cbmc/proofs/s2n_hmac_init/Makefile @@ -17,12 +17,16 @@ PROOF_UID = s2n_hmac_init HARNESS_ENTRY = $(PROOF_UID)_harness HARNESS_FILE = $(HARNESS_ENTRY).c +CBMC_OBJECT_BITS ?= 10 + PROOF_SOURCES += $(OPENSSL_SOURCE)/evp_override.c PROOF_SOURCES += $(OPENSSL_SOURCE)/md5_override.c PROOF_SOURCES += $(OPENSSL_SOURCE)/sha_override.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/s2n_is_in_fips_mode.c +PROOF_SOURCES += $(PROOF_STUB)/s2n_libcrypto_is_awslc.c +PROOF_SOURCES += $(PROOF_STUB)/darwin_check_fd_set_overflow.c PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROJECT_SOURCES += $(SRCDIR)/crypto/s2n_hash.c @@ -35,15 +39,9 @@ PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_low_level_hash_new REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_low_level_hash_reset REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_low_level_hash_free -REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_low_level_hash_digest -REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_low_level_hash_update REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_evp_hash_free REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_evp_hash_new REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_evp_hash_reset -REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_evp_hash_digest -REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_evp_hash_update -REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_evp_hash_allow_md5_for_fips -REMOVE_FUNCTION_BODY += s2n_hash_allow_md5_for_fips # The upper bound limit for these loops is me maximum possible value for xor_pad_size field # in struct s2n_hmac_state (128) plus one. See definition for struct s2n_hmac_state diff --git a/tests/cbmc/proofs/s2n_hmac_update/Makefile b/tests/cbmc/proofs/s2n_hmac_update/Makefile index e5ff4fbc15b..70701519f7c 100644 --- a/tests/cbmc/proofs/s2n_hmac_update/Makefile +++ b/tests/cbmc/proofs/s2n_hmac_update/Makefile @@ -37,7 +37,6 @@ PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c # We abstract these functions because manual inspection demonstrates they are unreachable. REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_evp_hash_digest -REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_hash_digest_size REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_hash_c_s2n_low_level_hash_digest UNWINDSET += diff --git a/tests/cbmc/proofs/s2n_mem_cleanup/Makefile b/tests/cbmc/proofs/s2n_mem_cleanup/Makefile index ca291de46fa..579f5a92400 100644 --- a/tests/cbmc/proofs/s2n_mem_cleanup/Makefile +++ b/tests/cbmc/proofs/s2n_mem_cleanup/Makefile @@ -29,8 +29,9 @@ PROOF_SOURCES += $(PROOF_STUB)/sysconf.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c -REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_mem_c_s2n_mem_cleanup_impl +REMOVE_FUNCTION_BODY += UNWINDSET += diff --git a/tests/cbmc/proofs/s2n_mem_init/Makefile b/tests/cbmc/proofs/s2n_mem_init/Makefile index ee3596eff9c..ee12bcc2c53 100644 --- a/tests/cbmc/proofs/s2n_mem_init/Makefile +++ b/tests/cbmc/proofs/s2n_mem_init/Makefile @@ -22,6 +22,7 @@ PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c # We abstract this function because manual inspection demonstrates it is unreachable. REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_mem_c_s2n_mem_cleanup_impl diff --git a/tests/cbmc/proofs/s2n_stuffer_alloc/Makefile b/tests/cbmc/proofs/s2n_stuffer_alloc/Makefile index db954284f82..bba60be57aa 100644 --- a/tests/cbmc/proofs/s2n_stuffer_alloc/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_alloc/Makefile @@ -26,6 +26,7 @@ PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c diff --git a/tests/cbmc/proofs/s2n_stuffer_alloc_ro_from_string/Makefile b/tests/cbmc/proofs/s2n_stuffer_alloc_ro_from_string/Makefile index 0e009f40207..4973e76a372 100644 --- a/tests/cbmc/proofs/s2n_stuffer_alloc_ro_from_string/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_alloc_ro_from_string/Makefile @@ -24,6 +24,8 @@ HARNESS_FILE = $(HARNESS_ENTRY).c PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_text.c diff --git a/tests/cbmc/proofs/s2n_stuffer_copy/Makefile b/tests/cbmc/proofs/s2n_stuffer_copy/Makefile index b9977b9e0c4..78fa8daf14d 100644 --- a/tests/cbmc/proofs/s2n_stuffer_copy/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_copy/Makefile @@ -24,6 +24,7 @@ PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c diff --git a/tests/cbmc/proofs/s2n_stuffer_extract_blob/Makefile b/tests/cbmc/proofs/s2n_stuffer_extract_blob/Makefile index 7aadbf68947..d756a5b2b32 100644 --- a/tests/cbmc/proofs/s2n_stuffer_extract_blob/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_extract_blob/Makefile @@ -23,6 +23,8 @@ PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c PROJECT_SOURCES += $(SRCDIR)/error/s2n_errno.c @@ -34,7 +36,7 @@ PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c # We abstract this function because manual inspection demonstrates it is unreachable. -REMOVE_FUNCTION_BODY += s2n_blob_slice +REMOVE_FUNCTION_BODY += UNWINDSET += diff --git a/tests/cbmc/proofs/s2n_stuffer_free/Makefile b/tests/cbmc/proofs/s2n_stuffer_free/Makefile index e486db6e5d8..dbfae88dcf5 100644 --- a/tests/cbmc/proofs/s2n_stuffer_free/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_free/Makefile @@ -31,6 +31,7 @@ PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_ensure.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c +PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c # We abstract this function because manual inspection demonstrates it is unreachable. REMOVE_FUNCTION_BODY += __CPROVER_file_local_s2n_mem_c_s2n_mem_cleanup_impl diff --git a/tests/cbmc/proofs/s2n_stuffer_growable_alloc/Makefile b/tests/cbmc/proofs/s2n_stuffer_growable_alloc/Makefile index 4a11bcc8189..e1ac1b1dde7 100644 --- a/tests/cbmc/proofs/s2n_stuffer_growable_alloc/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_growable_alloc/Makefile @@ -24,6 +24,7 @@ PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c diff --git a/tests/cbmc/proofs/s2n_stuffer_printf/s2n_stuffer_printf_harness.c b/tests/cbmc/proofs/s2n_stuffer_printf/s2n_stuffer_printf_harness.c index e1b5dbbd162..dacc51c8c57 100644 --- a/tests/cbmc/proofs/s2n_stuffer_printf/s2n_stuffer_printf_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_printf/s2n_stuffer_printf_harness.c @@ -23,6 +23,11 @@ int nondet_int(void); +/* The MacOS.sdk defines a macro called "vsnprintf" which breaks the + * function definition below, so make sure it's undef'd here. + */ +#undef vsnprintf + int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) { if (size > 0) diff --git a/tests/cbmc/proofs/s2n_stuffer_private_key_from_pem/Makefile b/tests/cbmc/proofs/s2n_stuffer_private_key_from_pem/Makefile index d5a9f516084..197796980c2 100644 --- a/tests/cbmc/proofs/s2n_stuffer_private_key_from_pem/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_private_key_from_pem/Makefile @@ -52,6 +52,6 @@ REMOVE_FUNCTION_BODY += s2n_add_overflow UNWINDSET += strlen.0:5 # size of S2N_PEM_PKCS1_RSA_PRIVATE_KEY UNWINDSET += strncmp.0:5 # size of S2N_PEM_END_TOKEN UNWINDSET += __CPROVER_file_local_s2n_stuffer_pem_c_s2n_stuffer_pem_read_contents.11:$(call addone,$(MAX_BLOB_SIZE)) -UNWINDSET += s2n_stuffer_skip_to_char.3:$(call addone,$(MAX_BLOB_SIZE)) +UNWINDSET += s2n_stuffer_skip_to_char.0:$(call addone,$(MAX_BLOB_SIZE)) include ../Makefile.common diff --git a/tests/cbmc/proofs/s2n_stuffer_raw_write/Makefile b/tests/cbmc/proofs/s2n_stuffer_raw_write/Makefile index 7f76ba4f14d..c6eecdec7ef 100644 --- a/tests/cbmc/proofs/s2n_stuffer_raw_write/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_raw_write/Makefile @@ -22,6 +22,9 @@ HARNESS_FILE = $(HARNESS_ENTRY).c PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c diff --git a/tests/cbmc/proofs/s2n_stuffer_read_base64/Makefile b/tests/cbmc/proofs/s2n_stuffer_read_base64/Makefile index 54d2b65cd2e..d58aa0bc120 100644 --- a/tests/cbmc/proofs/s2n_stuffer_read_base64/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_read_base64/Makefile @@ -26,6 +26,7 @@ PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c diff --git a/tests/cbmc/proofs/s2n_stuffer_read_hex/Makefile b/tests/cbmc/proofs/s2n_stuffer_read_hex/Makefile index 527e2146b25..de878adf70e 100644 --- a/tests/cbmc/proofs/s2n_stuffer_read_hex/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_read_hex/Makefile @@ -26,6 +26,9 @@ PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_hex.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c diff --git a/tests/cbmc/proofs/s2n_stuffer_read_line/Makefile b/tests/cbmc/proofs/s2n_stuffer_read_line/Makefile index 1da09371b7e..af71a41ee48 100644 --- a/tests/cbmc/proofs/s2n_stuffer_read_line/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_read_line/Makefile @@ -26,6 +26,7 @@ PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c diff --git a/tests/cbmc/proofs/s2n_stuffer_read_token/Makefile b/tests/cbmc/proofs/s2n_stuffer_read_token/Makefile index 011dd35381d..ab8c054b957 100644 --- a/tests/cbmc/proofs/s2n_stuffer_read_token/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_read_token/Makefile @@ -25,6 +25,8 @@ PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c diff --git a/tests/cbmc/proofs/s2n_stuffer_recv_from_fd/Makefile b/tests/cbmc/proofs/s2n_stuffer_recv_from_fd/Makefile index 33f0af8d613..d1dbe1ec2da 100644 --- a/tests/cbmc/proofs/s2n_stuffer_recv_from_fd/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_recv_from_fd/Makefile @@ -24,6 +24,7 @@ PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/read.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c diff --git a/tests/cbmc/proofs/s2n_stuffer_reserve/Makefile b/tests/cbmc/proofs/s2n_stuffer_reserve/Makefile index 0a042b5988a..b60c92f86f6 100644 --- a/tests/cbmc/proofs/s2n_stuffer_reserve/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_reserve/Makefile @@ -24,6 +24,7 @@ PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c diff --git a/tests/cbmc/proofs/s2n_stuffer_reserve_space/Makefile b/tests/cbmc/proofs/s2n_stuffer_reserve_space/Makefile index 6f34f7f018f..58669d39a4f 100644 --- a/tests/cbmc/proofs/s2n_stuffer_reserve_space/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_reserve_space/Makefile @@ -25,6 +25,9 @@ PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROJECT_SOURCES += $(SRCDIR)/error/s2n_errno.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c diff --git a/tests/cbmc/proofs/s2n_stuffer_reserve_uint16/Makefile b/tests/cbmc/proofs/s2n_stuffer_reserve_uint16/Makefile index ea6cf169256..58354660f67 100644 --- a/tests/cbmc/proofs/s2n_stuffer_reserve_uint16/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_reserve_uint16/Makefile @@ -23,6 +23,8 @@ PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c diff --git a/tests/cbmc/proofs/s2n_stuffer_reserve_uint24/Makefile b/tests/cbmc/proofs/s2n_stuffer_reserve_uint24/Makefile index e3f4f6e310c..38f0f17b27a 100644 --- a/tests/cbmc/proofs/s2n_stuffer_reserve_uint24/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_reserve_uint24/Makefile @@ -23,6 +23,8 @@ PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c diff --git a/tests/cbmc/proofs/s2n_stuffer_resize/Makefile b/tests/cbmc/proofs/s2n_stuffer_resize/Makefile index 7be49e367f7..4dbaa98db62 100644 --- a/tests/cbmc/proofs/s2n_stuffer_resize/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_resize/Makefile @@ -24,6 +24,7 @@ PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c diff --git a/tests/cbmc/proofs/s2n_stuffer_resize_if_empty/Makefile b/tests/cbmc/proofs/s2n_stuffer_resize_if_empty/Makefile index 41b69d86b55..5f5e673310d 100644 --- a/tests/cbmc/proofs/s2n_stuffer_resize_if_empty/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_resize_if_empty/Makefile @@ -22,6 +22,8 @@ HARNESS_FILE = $(HARNESS_ENTRY).c PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROJECT_SOURCES += $(SRCDIR)/error/s2n_errno.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c diff --git a/tests/cbmc/proofs/s2n_stuffer_skip_write/Makefile b/tests/cbmc/proofs/s2n_stuffer_skip_write/Makefile index 11735a22923..933228c81ad 100644 --- a/tests/cbmc/proofs/s2n_stuffer_skip_write/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_skip_write/Makefile @@ -24,6 +24,7 @@ PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c diff --git a/tests/cbmc/proofs/s2n_stuffer_write/Makefile b/tests/cbmc/proofs/s2n_stuffer_write/Makefile index e326b10df92..ad39ed44ebb 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_write/Makefile @@ -22,6 +22,9 @@ HARNESS_FILE = $(HARNESS_ENTRY).c PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c diff --git a/tests/cbmc/proofs/s2n_stuffer_write_base64/Makefile b/tests/cbmc/proofs/s2n_stuffer_write_base64/Makefile index 44f07ca60c2..e8cea785c8b 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write_base64/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_write_base64/Makefile @@ -26,6 +26,7 @@ PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c diff --git a/tests/cbmc/proofs/s2n_stuffer_write_bytes/Makefile b/tests/cbmc/proofs/s2n_stuffer_write_bytes/Makefile index 0a1bedffa74..3bc38d997c8 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write_bytes/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_write_bytes/Makefile @@ -22,6 +22,9 @@ HARNESS_FILE = $(HARNESS_ENTRY).c PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c diff --git a/tests/cbmc/proofs/s2n_stuffer_write_hex/Makefile b/tests/cbmc/proofs/s2n_stuffer_write_hex/Makefile index a09b7f8a6e8..c6c8c1d3974 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write_hex/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_write_hex/Makefile @@ -26,6 +26,9 @@ PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_hex.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c diff --git a/tests/cbmc/proofs/s2n_stuffer_write_network_order/Makefile b/tests/cbmc/proofs/s2n_stuffer_write_network_order/Makefile index ba093b9041a..4ac20af933e 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write_network_order/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_write_network_order/Makefile @@ -22,6 +22,8 @@ PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c diff --git a/tests/cbmc/proofs/s2n_stuffer_write_uint16/Makefile b/tests/cbmc/proofs/s2n_stuffer_write_uint16/Makefile index de513abf4e9..80ad99457f6 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write_uint16/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_write_uint16/Makefile @@ -23,6 +23,9 @@ HARNESS_FILE = $(HARNESS_ENTRY).c PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_network_order.c diff --git a/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/Makefile b/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/Makefile index 255787c81b9..0fd96bd3162 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/Makefile @@ -23,6 +23,9 @@ PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_hex.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c diff --git a/tests/cbmc/proofs/s2n_stuffer_write_uint24/Makefile b/tests/cbmc/proofs/s2n_stuffer_write_uint24/Makefile index 0ee330db573..d5f36fd153f 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write_uint24/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_write_uint24/Makefile @@ -23,6 +23,9 @@ HARNESS_FILE = $(HARNESS_ENTRY).c PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_network_order.c diff --git a/tests/cbmc/proofs/s2n_stuffer_write_uint32/Makefile b/tests/cbmc/proofs/s2n_stuffer_write_uint32/Makefile index bec46adf7eb..c0ef76ced01 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write_uint32/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_write_uint32/Makefile @@ -23,6 +23,9 @@ HARNESS_FILE = $(HARNESS_ENTRY).c PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_network_order.c diff --git a/tests/cbmc/proofs/s2n_stuffer_write_uint64/Makefile b/tests/cbmc/proofs/s2n_stuffer_write_uint64/Makefile index 8e339851469..08465311f7b 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write_uint64/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_write_uint64/Makefile @@ -23,6 +23,9 @@ HARNESS_FILE = $(HARNESS_ENTRY).c PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_network_order.c diff --git a/tests/cbmc/proofs/s2n_stuffer_write_uint8/Makefile b/tests/cbmc/proofs/s2n_stuffer_write_uint8/Makefile index 82fc51dd59f..975c8af6cf9 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write_uint8/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_write_uint8/Makefile @@ -22,6 +22,9 @@ HARNESS_FILE = $(HARNESS_ENTRY).c PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_network_order.c diff --git a/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/Makefile b/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/Makefile index 6a5695128fc..b943356821f 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/Makefile +++ b/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/Makefile @@ -23,6 +23,9 @@ PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE) PROOF_SOURCES += $(PROOF_SOURCE)/cbmc_utils.c PROOF_SOURCES += $(PROOF_SOURCE)/make_common_datastructures.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c +PROOF_SOURCES += $(PROOF_STUB)/mlock.c +PROOF_SOURCES += $(PROOF_STUB)/munlock.c +PROOF_SOURCES += $(PROOF_STUB)/madvise.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer_hex.c PROJECT_SOURCES += $(SRCDIR)/stuffer/s2n_stuffer.c diff --git a/tests/cbmc/sources/cbmc_utils.c b/tests/cbmc/sources/cbmc_utils.c index d544391882c..733c87fb9e1 100644 --- a/tests/cbmc/sources/cbmc_utils.c +++ b/tests/cbmc/sources/cbmc_utils.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /** @@ -80,7 +81,7 @@ void assert_bytes_match(const uint8_t *const a, const uint8_t *const b, const si assert(!a == !b); if (len > 0 && a != NULL && b != NULL) { size_t i; - __CPROVER_assume(i < len && len < MAX_MALLOC); /* prevent spurious pointer overflows */ + CONTRACT_ASSUME(i < len && len < MAX_MALLOC); /* prevent spurious pointer overflows */ assert(a[ i ] == b[ i ]); } } @@ -89,7 +90,7 @@ void assert_all_bytes_are(const uint8_t *const a, const uint8_t c, const size_t { if (len > 0 && a != NULL) { size_t i; - __CPROVER_assume(i < len); + CONTRACT_ASSUME(i < len); assert(a[ i ] == c); } } @@ -110,7 +111,7 @@ void save_byte_from_array(const uint8_t *const array, const size_t size, struct { if (size > 0 && array && storage) { storage->idx = nondet_size_t(); - __CPROVER_assume(storage->idx < size); + CONTRACT_ASSUME(storage->idx < size); storage->byte = array[ storage->idx ]; } } @@ -137,11 +138,11 @@ int uninterpreted_compare(const void *const a, const void *const b) assert(b != NULL); int rval = __CPROVER_uninterpreted_compare(a, b); /* Compare is reflexive */ - __CPROVER_assume(IMPLIES(a == b, rval == 0)); + CONTRACT_ASSUME(IMPLIES(a == b, rval == 0)); /* Compare is anti-symmetric*/ - __CPROVER_assume(__CPROVER_uninterpreted_compare(b, a) == -rval); + CONTRACT_ASSUME(__CPROVER_uninterpreted_compare(b, a) == -rval); /* If two things are equal, their hashes are also equal */ - if (rval == 0) { __CPROVER_assume(__CPROVER_uninterpreted_hasher(a) == __CPROVER_uninterpreted_hasher(b)); } + if (rval == 0) { CONTRACT_ASSUME(__CPROVER_uninterpreted_hasher(a) == __CPROVER_uninterpreted_hasher(b)); } return rval; } @@ -160,11 +161,11 @@ bool uninterpreted_equals(const void *const a, const void *const b) { bool rval = __CPROVER_uninterpreted_equals(a, b); /* Equals is reflexive */ - __CPROVER_assume(IMPLIES(a == b, rval)); + CONTRACT_ASSUME(IMPLIES(a == b, rval)); /* Equals is symmetric */ - __CPROVER_assume(__CPROVER_uninterpreted_equals(b, a) == rval); + CONTRACT_ASSUME(__CPROVER_uninterpreted_equals(b, a) == rval); /* If two things are equal, their hashes are also equal */ - if (rval) { __CPROVER_assume(__CPROVER_uninterpreted_hasher(a) == __CPROVER_uninterpreted_hasher(b)); } + if (rval) { CONTRACT_ASSUME(__CPROVER_uninterpreted_hasher(a) == __CPROVER_uninterpreted_hasher(b)); } return rval; } diff --git a/tests/cbmc/stubs/darwin_check_fd_set_overflow.c b/tests/cbmc/stubs/darwin_check_fd_set_overflow.c new file mode 100644 index 00000000000..e1130b4f8d9 --- /dev/null +++ b/tests/cbmc/stubs/darwin_check_fd_set_overflow.c @@ -0,0 +1,25 @@ +/* + * 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 + +/* A reference to this function appears as a side-effect of */ +/* building on macOS with some versions of XCode installed */ +/* A dummy stub is supplied here to prevent CBMC complaining */ +/* of a missing body. */ +int __darwin_check_fd_set_overflow(int x, const void *y, int z) +{ + return nondet_int(); +} diff --git a/tests/cbmc/stubs/s2n_is_in_fips_mode.c b/tests/cbmc/stubs/s2n_is_in_fips_mode.c index 7ee61b05510..54b45dc71d2 100644 --- a/tests/cbmc/stubs/s2n_is_in_fips_mode.c +++ b/tests/cbmc/stubs/s2n_is_in_fips_mode.c @@ -17,7 +17,7 @@ #include "crypto/s2n_fips.h" -static bool flag = 0; +static bool s2n_fips_mode_flag = 0; static bool s2n_fips_mode_enabled = 0; /** @@ -26,9 +26,9 @@ static bool s2n_fips_mode_enabled = 0; */ bool s2n_is_in_fips_mode() { - if (flag == 0) { + if (s2n_fips_mode_flag == 0) { s2n_fips_mode_enabled = nondet_bool() ? 1 : 0; - flag = 1; + s2n_fips_mode_flag = 1; } return s2n_fips_mode_enabled; } diff --git a/utils/s2n_ensure.h b/utils/s2n_ensure.h index 890cca162b5..866db937db0 100644 --- a/utils/s2n_ensure.h +++ b/utils/s2n_ensure.h @@ -135,15 +135,19 @@ void *s2n_ensure_memmove_trace(void *to, const void *from, size_t size); * Violations of the function contracts are undefined behaviour. */ #ifdef CBMC + #define CONTRACT_ASSERT(...) __CPROVER_assert(__VA_ARGS__) #define CONTRACT_ASSIGNS(...) __CPROVER_assigns(__VA_ARGS__) #define CONTRACT_ASSIGNS_ERR(...) CONTRACT_ASSIGNS(__VA_ARGS__, _s2n_debug_info, s2n_errno) + #define CONTRACT_ASSUME(...) __CPROVER_assume(__VA_ARGS__) #define CONTRACT_REQUIRES(...) __CPROVER_requires(__VA_ARGS__) #define CONTRACT_ENSURES(...) __CPROVER_ensures(__VA_ARGS__) #define CONTRACT_INVARIANT(...) __CPROVER_loop_invariant(__VA_ARGS__) #define CONTRACT_RETURN_VALUE (__CPROVER_return_value) #else + #define CONTRACT_ASSERT(...) #define CONTRACT_ASSIGNS(...) #define CONTRACT_ASSIGNS_ERR(...) + #define CONTRACT_ASSUME(...) #define CONTRACT_REQUIRES(...) #define CONTRACT_ENSURES(...) #define CONTRACT_INVARIANT(...) From fbc2d06cbeda6c32bd8b45b54cdeda89f4e00000 Mon Sep 17 00:00:00 2001 From: Jou Ho <43765840+jouho@users.noreply.github.com> Date: Fri, 2 Aug 2024 15:31:11 -0700 Subject: [PATCH 05/19] fix: zip corpus files before uploading to s3 (#4685) --- tests/fuzz/runFuzzTest.sh | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/fuzz/runFuzzTest.sh b/tests/fuzz/runFuzzTest.sh index f3d723d8537..17194030f19 100755 --- a/tests/fuzz/runFuzzTest.sh +++ b/tests/fuzz/runFuzzTest.sh @@ -84,8 +84,15 @@ if [ "$CORPUS_UPLOAD_LOC" != "none" ]; then # The LD variables interferes with certificate validation when communicating with AWS S3. unset LD_PRELOAD unset LD_LIBRARY_PATH - printf "Copying corpus files from S3 bucket...\n" - aws s3 sync $CORPUS_UPLOAD_LOC/${TEST_NAME}/ "${TEMP_CORPUS_DIR}" + + # Check if corpus.zip exists in the specified S3 location. + # `> /dev/null 2>&1` redirects output to /dev/null. + # If the file is not found, `aws s3 ls` returns a non-zero exit code. + if aws s3 ls "${CORPUS_UPLOAD_LOC}/${TEST_NAME}/corpus.zip" > /dev/null 2>&1; then + printf "corpus.zip found, downloading from S3 bucket and unzipping...\n" + aws s3 cp "${CORPUS_UPLOAD_LOC}/${TEST_NAME}/corpus.zip" "${TEMP_CORPUS_DIR}/corpus.zip" + unzip -o "${TEMP_CORPUS_DIR}/corpus.zip" -d "${TEMP_CORPUS_DIR}" + fi ) else cp -r ./corpus/${TEST_NAME}/. "${TEMP_CORPUS_DIR}" @@ -225,8 +232,11 @@ then unset LD_PRELOAD unset LD_LIBRARY_PATH if [ "$CORPUS_UPLOAD_LOC" != "none" ]; then - printf "Uploading corpus files to S3 bucket...\n" - aws s3 sync ./corpus/${TEST_NAME}/ $CORPUS_UPLOAD_LOC/${TEST_NAME}/ + printf "Zipping corpus files...\n" + zip -r ./corpus/${TEST_NAME}.zip ./corpus/${TEST_NAME}/ + + printf "Uploading zipped corpus file to S3 bucket...\n" + aws s3 cp ./corpus/${TEST_NAME}.zip $CORPUS_UPLOAD_LOC/${TEST_NAME}/corpus.zip fi fi From 9886a3cfa001c97ce5b281f6377a737f2ec99cd6 Mon Sep 17 00:00:00 2001 From: Lindsay Stewart Date: Mon, 5 Aug 2024 13:45:36 -0700 Subject: [PATCH 06/19] docs: update blinding docs (#4686) --- docs/usage-guide/topics/ch03-error-handling.md | 12 ++++++++---- docs/usage-guide/topics/ch07-io.md | 2 ++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/usage-guide/topics/ch03-error-handling.md b/docs/usage-guide/topics/ch03-error-handling.md index f32641175a7..7439e9361b3 100644 --- a/docs/usage-guide/topics/ch03-error-handling.md +++ b/docs/usage-guide/topics/ch03-error-handling.md @@ -61,13 +61,17 @@ if (s2n_negotiate(conn, &blocked) < 0) { } ``` -### Blinding +## Blinding -Blinding is a mitigation against timing side-channels which in some cases can leak information about encrypted data. By default s2n-tls will cause a thread to sleep between 10 and 30 seconds whenever tampering is detected. +Blinding is a mitigation against timing side-channels which in some cases can leak information about encrypted data. [This](https://aws.amazon.com/blogs/security/s2n-and-lucky-13/) blog post includes a good description of blinding and how it mitigates timing side-channels like Lucky13. -Setting the `S2N_SELF_SERVICE_BLINDING` option with `s2n_connection_set_blinding()` turns off this behavior. This is useful for applications that are handling many connections in a single thread. In that case, if `s2n_recv()` or `s2n_negotiate()` return an error, self-service applications must call `s2n_connection_get_delay()` and pause activity on the connection for the specified number of nanoseconds before calling `close()` or `shutdown()`. `s2n_shutdown()` will fail if called before the blinding delay elapses. +By default s2n-tls will cause a thread to sleep between 10 and 30 seconds whenever an error triggered by the peer occurs. The default implementation of blinding blocks the thread. -### Stacktraces +For non-blocking blinding, an application can handle the delay itself. This is useful for applications that are handling many connections in a single thread. The application should set the `S2N_SELF_SERVICE_BLINDING` option with `s2n_connection_set_blinding()`. After `s2n_recv()` or `s2n_negotiate()` return an error, the application must call `s2n_connection_get_delay()` and pause activity on the connection for the specified number of nanoseconds before calling `s2n_shutdown()`, `close()`, or `shutdown()`. `s2n_shutdown()` will fail if called before the blinding delay elapses. To correctly implement self-service blinding, the application must have nanosecond-level resolution on its implementation of the delay. Not properly implementing self-service blinding (such as by not waiting for the full delay before calling `close()` on the underlying socket) makes an application potentially vulnerable to timing side-channel attacks. + +The maximum blinding delay is configurable via `s2n_config_set_max_blinding_delay()`. However, setting a maximum delay lower than the recommended default (30s) will make timing side-channel attacks easier. The lower the delay, the fewer requests and less total time an attacker will need to execute a side-channel attack. If a lower delay is required for reasons such as client timeouts, then the highest value practically possible should be chosen to limit risk. Do not lower the blinding delay without fully understanding the risks. + +## Stacktraces s2n-tls has an mechanism to capture stacktraces when errors occur. This mechanism is off by default, but can be enabled in code by calling `s2n_stack_traces_enabled_set()`. It can be enabled globally by setting the environment variable `S2N_PRINT_STACKTRACE=1`. diff --git a/docs/usage-guide/topics/ch07-io.md b/docs/usage-guide/topics/ch07-io.md index 65e27db8acd..0040559147b 100644 --- a/docs/usage-guide/topics/ch07-io.md +++ b/docs/usage-guide/topics/ch07-io.md @@ -51,6 +51,8 @@ if (flags < 0) return S2N_FAILURE; if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) return S2N_FAILURE; ``` +Note: If an application requires non-blocking IO, it likely also requires self-service blinding. See [Blinding](./ch03-error-handling.md#Blinding). + ## Errors and Alerts If the peer sends an alert, the next call to a read IO method will report **S2N_FAILURE** and From e837ea3d616110979380359ea9309c8a8061e8b5 Mon Sep 17 00:00:00 2001 From: Cameron Bytheway Date: Mon, 5 Aug 2024 19:25:37 -0600 Subject: [PATCH 07/19] fix(bindings): enforce waker contract on `poll` operations (#4688) --- bindings/rust/s2n-tls-tokio/src/lib.rs | 170 ++++++++++-------- bindings/rust/s2n-tls-tokio/src/task/mod.rs | 4 + bindings/rust/s2n-tls-tokio/src/task/waker.rs | 6 + .../s2n-tls-tokio/src/task/waker/contract.rs | 168 +++++++++++++++++ .../rust/s2n-tls-tokio/tests/handshake.rs | 73 ++++++++ bindings/rust/s2n-tls/src/connection.rs | 39 +++- bindings/rust/s2n-tls/src/testing.rs | 3 +- 7 files changed, 375 insertions(+), 88 deletions(-) create mode 100644 bindings/rust/s2n-tls-tokio/src/task/mod.rs create mode 100644 bindings/rust/s2n-tls-tokio/src/task/waker.rs create mode 100644 bindings/rust/s2n-tls-tokio/src/task/waker/contract.rs diff --git a/bindings/rust/s2n-tls-tokio/src/lib.rs b/bindings/rust/s2n-tls-tokio/src/lib.rs index 4d78b88236d..4295eb9b8eb 100644 --- a/bindings/rust/s2n-tls-tokio/src/lib.rs +++ b/bindings/rust/s2n-tls-tokio/src/lib.rs @@ -24,6 +24,10 @@ use tokio::{ time::{sleep, Duration, Sleep}, }; +// TODO use the version from s2n_quic_core +mod task; +use task::waker::debug_assert_contract as debug_assert_waker_contract; + macro_rules! ready { ($x:expr) => { match $x { @@ -105,37 +109,39 @@ where type Output = Result<(), Error>; fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { - // Retrieve a result, either from the stored error - // or by polling Connection::poll_negotiate(). - // Connection::poll_negotiate() only completes once, - // regardless of how often this method is polled. - let result = match self.error.take() { - Some(err) => Err(err), - None => { - let handshake_poll = self.tls.with_io(ctx, |context| { - let conn = context.get_mut().as_mut(); - conn.poll_negotiate().map(|r| r.map(|_| ())) - }); - ready!(handshake_poll) - } - }; - // If the result isn't a fatal error, return it immediately. - // Otherwise, poll Connection::poll_shutdown(). - // - // Shutdown is only best-effort. - // When Connection::poll_shutdown() completes, even with an error, - // we return the original Connection::poll_negotiate() error. - match result { - Ok(r) => Ok(r).into(), - Err(e) if e.is_retryable() => Err(e).into(), - Err(e) => match Pin::new(&mut self.tls).poll_shutdown(ctx) { - Pending => { - self.error = Some(e); - Pending + debug_assert_waker_contract(ctx, |ctx| { + // Retrieve a result, either from the stored error + // or by polling Connection::poll_negotiate(). + // Connection::poll_negotiate() only completes once, + // regardless of how often this method is polled. + let result = match self.error.take() { + Some(err) => Err(err), + None => { + let handshake_poll = self.tls.with_io(ctx, |context| { + let conn = context.get_mut().as_mut(); + conn.poll_negotiate().map(|r| r.map(|_| ())) + }); + ready!(handshake_poll) } - Ready(_) => Err(e).into(), - }, - } + }; + // If the result isn't a fatal error, return it immediately. + // Otherwise, poll Connection::poll_shutdown(). + // + // Shutdown is only best-effort. + // When Connection::poll_shutdown() completes, even with an error, + // we return the original Connection::poll_negotiate() error. + match result { + Ok(r) => Ok(r).into(), + Err(e) if e.is_retryable() => Err(e).into(), + Err(e) => match Pin::new(&mut self.tls).poll_shutdown(ctx) { + Pending => { + self.error = Some(e); + Pending + } + Ready(_) => Err(e).into(), + }, + } + }) } } @@ -219,7 +225,11 @@ where let mut async_context = Context::from_waker(tls.conn.as_ref().waker().unwrap()); let stream = Pin::new(&mut tls.stream); - match action(stream, &mut async_context) { + let res = debug_assert_waker_contract(&mut async_context, |async_context| { + action(stream, async_context) + }); + + match res { Poll::Ready(Ok(len)) => len as c_int, Poll::Pending => { set_errno(Errno(libc::EWOULDBLOCK)); @@ -258,24 +268,26 @@ where /// `poll_blinding` or `poll_shutdown` (which calls `poll_blinding` /// internally) returns ready. pub fn poll_blinding(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { - let tls = self.get_mut(); + debug_assert_waker_contract(ctx, |ctx| { + let tls = self.get_mut(); + + if tls.blinding.is_none() { + let delay = tls.as_ref().remaining_blinding_delay()?; + if !delay.is_zero() { + // Sleep operates at the milisecond resolution, so add an extra + // millisecond to account for any stray nanoseconds. + let safety = Duration::from_millis(1); + tls.blinding = Some(Box::pin(sleep(delay.saturating_add(safety)))); + } + }; - if tls.blinding.is_none() { - let delay = tls.as_ref().remaining_blinding_delay()?; - if !delay.is_zero() { - // Sleep operates at the milisecond resolution, so add an extra - // millisecond to account for any stray nanoseconds. - let safety = Duration::from_millis(1); - tls.blinding = Some(Box::pin(sleep(delay.saturating_add(safety)))); + if let Some(timer) = tls.blinding.as_mut() { + ready!(timer.as_mut().poll(ctx)); + tls.blinding = None; } - }; - if let Some(timer) = tls.blinding.as_mut() { - ready!(timer.as_mut().poll(ctx)); - tls.blinding = None; - } - - Poll::Ready(Ok(())) + Poll::Ready(Ok(())) + }) } pub async fn apply_blinding(&mut self) -> Result<(), Error> { @@ -362,39 +374,41 @@ where } fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { - ready!(self.as_mut().poll_blinding(ctx))?; - - // s2n_shutdown_send must not be called again if it errors - if self.shutdown_error.is_none() { - let result = ready!(self.as_mut().with_io(ctx, |mut context| { - context - .conn - .as_mut() - .poll_shutdown_send() - .map(|r| r.map(|_| ())) - })); - if let Err(error) = result { - self.shutdown_error = Some(error); - // s2n_shutdown_send only writes, so will never trigger blinding again. - // So we do not need to poll_blinding again after this error. + debug_assert_waker_contract(ctx, |ctx| { + ready!(self.as_mut().poll_blinding(ctx))?; + + // s2n_shutdown_send must not be called again if it errors + if self.shutdown_error.is_none() { + let result = ready!(self.as_mut().with_io(ctx, |mut context| { + context + .conn + .as_mut() + .poll_shutdown_send() + .map(|r| r.map(|_| ())) + })); + if let Err(error) = result { + self.shutdown_error = Some(error); + // s2n_shutdown_send only writes, so will never trigger blinding again. + // So we do not need to poll_blinding again after this error. + } + }; + + let tcp_result = ready!(Pin::new(&mut self.as_mut().stream).poll_shutdown(ctx)); + + if let Some(err) = self.shutdown_error.take() { + // poll methods shouldn't be called again after returning Ready, but + // nothing actually prevents it so poll_shutdown should handle it. + // s2n_shutdown can be polled indefinitely after succeeding, but not after failing. + // s2n_tls::error::Error isn't cloneable, so we can't just return the same error + // if poll_shutdown is called again. Instead, save a different error. + let next_error = Error::application("Shutdown called again after error".into()); + self.shutdown_error = Some(next_error); + + Ready(Err(io::Error::from(err))) + } else { + Ready(tcp_result) } - }; - - let tcp_result = ready!(Pin::new(&mut self.as_mut().stream).poll_shutdown(ctx)); - - if let Some(err) = self.shutdown_error.take() { - // poll methods shouldn't be called again after returning Ready, but - // nothing actually prevents it so poll_shutdown should handle it. - // s2n_shutdown can be polled indefinitely after succeeding, but not after failing. - // s2n_tls::error::Error isn't cloneable, so we can't just return the same error - // if poll_shutdown is called again. Instead, save a different error. - let next_error = Error::application("Shutdown called again after error".into()); - self.shutdown_error = Some(next_error); - - Ready(Err(io::Error::from(err))) - } else { - Ready(tcp_result) - } + }) } } diff --git a/bindings/rust/s2n-tls-tokio/src/task/mod.rs b/bindings/rust/s2n-tls-tokio/src/task/mod.rs new file mode 100644 index 00000000000..a2fc259e2b6 --- /dev/null +++ b/bindings/rust/s2n-tls-tokio/src/task/mod.rs @@ -0,0 +1,4 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +pub mod waker; diff --git a/bindings/rust/s2n-tls-tokio/src/task/waker.rs b/bindings/rust/s2n-tls-tokio/src/task/waker.rs new file mode 100644 index 00000000000..b7157e2bd68 --- /dev/null +++ b/bindings/rust/s2n-tls-tokio/src/task/waker.rs @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +mod contract; + +pub use contract::*; diff --git a/bindings/rust/s2n-tls-tokio/src/task/waker/contract.rs b/bindings/rust/s2n-tls-tokio/src/task/waker/contract.rs new file mode 100644 index 00000000000..4f9368da535 --- /dev/null +++ b/bindings/rust/s2n-tls-tokio/src/task/waker/contract.rs @@ -0,0 +1,168 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use core::{ + sync::atomic::{AtomicBool, Ordering}, + task::{Context, Poll, Waker}, +}; +use std::{sync::Arc, task::Wake}; + +/// Checks that if a function returns [`Poll::Pending`], then the function called [`Waker::clone`], +/// [`Waker::wake`], or [`Waker::wake_by_ref`] on the [`Context`]'s [`Waker`]. +pub struct Contract { + state: Arc, + waker: Waker, +} + +struct State { + inner: Waker, + wake_called: AtomicBool, +} + +impl Wake for State { + #[inline] + fn wake(self: Arc) { + Wake::wake_by_ref(&self) + } + + #[inline] + fn wake_by_ref(self: &Arc) { + self.wake_called.store(true, Ordering::Release); + self.inner.wake_by_ref(); + } +} + +impl Contract { + /// Wraps a [`Context`] in the contract checker + #[inline] + pub fn new(cx: &mut Context) -> Self { + let state = State { + inner: cx.waker().clone(), + wake_called: AtomicBool::new(false), + }; + let state = Arc::new(state); + let waker = Waker::from(state.clone()); + Self { state, waker } + } + + /// Returns a new [`Context`] to be checked + #[inline] + pub fn context(&self) -> Context { + Context::from_waker(&self.waker) + } + + /// Checks the state of the waker based on the provided `outcome` + #[inline] + #[track_caller] + pub fn check_outcome(self, outcome: &Poll) { + if outcome.is_ready() { + return; + } + + let strong_count = Arc::strong_count(&self.state); + let is_cloned = strong_count > 2; // 1 for `state`, one for our owned `waker` + let wake_called = self.state.wake_called.load(Ordering::Acquire); + + let is_ok = is_cloned || wake_called; + + assert!( + is_ok, + "strong_count = {strong_count}; is_cloned = {is_cloned}; wake_called = {wake_called}" + ); + } +} + +/// Checks that if a function returns [`Poll::Pending`], then the function called [`Waker::clone`], +/// [`Waker::wake`], or [`Waker::wake_by_ref`] on the [`Context`]'s [`Waker`]. +#[inline(always)] +#[track_caller] +pub fn assert_contract Poll, R>(cx: &mut Context, f: F) -> Poll { + let contract = Contract::new(cx); + let mut cx = contract.context(); + let outcome = f(&mut cx); + contract.check_outcome(&outcome); + outcome +} + +/// Checks that if a function returns [`Poll::Pending`], then the function called [`Waker::clone`], +/// [`Waker::wake`], or [`Waker::wake_by_ref`] on the [`Context`]'s [`Waker`]. +/// +/// This is only enabled with `debug_assertions`. +#[inline(always)] +#[track_caller] +pub fn debug_assert_contract Poll, R>( + cx: &mut Context, + f: F, +) -> Poll { + #[cfg(debug_assertions)] + return assert_contract(cx, f); + + #[cfg(not(debug_assertions))] + return f(cx); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[inline] + pub fn noop() -> Waker { + use core::{ + ptr, + task::{RawWaker, RawWakerVTable}, + }; + + const VTABLE: RawWakerVTable = RawWakerVTable::new( + // Cloning just returns a new no-op raw waker + |_| RAW, + // `wake` does nothing + |_| {}, + // `wake_by_ref` does nothing + |_| {}, + // Dropping does nothing as we don't allocate anything + |_| {}, + ); + const RAW: RawWaker = RawWaker::new(ptr::null(), &VTABLE); + + unsafe { Waker::from_raw(RAW) } + } + + #[test] + fn correct_test() { + let waker = noop(); + let mut cx = Context::from_waker(&waker); + + // the contract isn't violated when returning Ready + let _ = assert_contract(&mut cx, |_cx| Poll::Ready(())); + + // the contract isn't violated if the waker is immediately woken + let _ = assert_contract(&mut cx, |cx| { + cx.waker().wake_by_ref(); + Poll::<()>::Pending + }); + + // the contract isn't violated if the waker is cloned then immediately woken + let _ = assert_contract(&mut cx, |cx| { + let waker = cx.waker().clone(); + waker.wake(); + Poll::<()>::Pending + }); + + // the contract isn't violated if the waker is cloned and stored for later + let mut stored = None; + let _ = assert_contract(&mut cx, |cx| { + stored = Some(cx.waker().clone()); + Poll::<()>::Pending + }); + } + + #[test] + #[should_panic] + fn incorrect_test() { + let waker = noop(); + let mut cx = Context::from_waker(&waker); + + // the contract is violated if we return Pending without doing anything + let _ = assert_contract(&mut cx, |_cx| Poll::<()>::Pending); + } +} diff --git a/bindings/rust/s2n-tls-tokio/tests/handshake.rs b/bindings/rust/s2n-tls-tokio/tests/handshake.rs index 84669f746c6..6ad15116601 100644 --- a/bindings/rust/s2n-tls-tokio/tests/handshake.rs +++ b/bindings/rust/s2n-tls-tokio/tests/handshake.rs @@ -195,3 +195,76 @@ async fn io_stream_access() -> Result<(), Box> { Ok(()) } + +#[tokio::test] +async fn handshake_with_async_callback() -> Result<(), Box> { + use core::{future::Future, pin::Pin, task::Poll}; + use s2n_tls::{callbacks::*, connection, error}; + use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _}; + + let client_config = common::client_config()?.build()?; + + let mut server_config = common::server_config()?; + server_config.set_client_hello_callback(DelayClientHelloHandler { + amount: Duration::from_secs(1), + })?; + let server_config = server_config.build()?; + + let client = TlsConnector::new(client_config.clone()); + let server = TlsAcceptor::new(server_config.clone()); + let (server_stream, client_stream) = common::get_streams().await?; + + let mut tasks = tokio::task::JoinSet::new(); + + tasks.spawn(async move { + let mut stream = client.connect("localhost", client_stream).await.unwrap(); + stream.shutdown().await.unwrap(); + let len = stream.read(&mut [0]).await.unwrap(); + assert_eq!(len, 0); + }); + + tasks.spawn(async move { + let mut stream = server.accept(server_stream).await.unwrap(); + stream.shutdown().await.unwrap(); + let len = stream.read(&mut [0]).await.unwrap(); + assert_eq!(len, 0); + }); + + // make sure the tasks completed + while let Some(res) = tasks.join_next().await { + res.unwrap(); + } + + /// Adds an artificial delay to a ClientHello callback + #[derive(Clone)] + pub struct DelayClientHelloHandler { + amount: Duration, + } + + impl ClientHelloCallback for DelayClientHelloHandler { + fn on_client_hello( + &self, + _connection: &mut connection::Connection, + ) -> Result>>, error::Error> { + Ok(Some(Box::pin(DelayClientHelloFuture { + timer: Box::pin(tokio::time::sleep(self.amount)), + }))) + } + } + + pub struct DelayClientHelloFuture { + timer: Pin>, + } + + impl ConnectionFuture for DelayClientHelloFuture { + fn poll( + mut self: Pin<&mut Self>, + _connection: &mut connection::Connection, + ctx: &mut core::task::Context, + ) -> Poll> { + self.timer.as_mut().poll(ctx).map(Ok) + } + } + + Ok(()) +} diff --git a/bindings/rust/s2n-tls/src/connection.rs b/bindings/rust/s2n-tls/src/connection.rs index 766a248d5ff..9e70bcd8fee 100644 --- a/bindings/rust/s2n-tls/src/connection.rs +++ b/bindings/rust/s2n-tls/src/connection.rs @@ -521,20 +521,41 @@ impl Connection { }) } - pub(crate) fn poll_negotiate_method(&mut self, negotiate: F) -> Poll> + pub(crate) fn poll_negotiate_method( + &mut self, + mut negotiate: F, + ) -> Poll> where - F: FnOnce(&mut Connection) -> Poll>, + F: FnMut(&mut Connection) -> Poll>, { self.trigger_initializer(); - // Check whether renegotiate is blocked by any async callbacks - match self.poll_async_task().unwrap_or(Poll::Ready(Ok(()))) { - Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), - Poll::Pending => return Poll::Pending, - Poll::Ready(Ok(_)) => {} - }; + loop { + // Check whether renegotiate is blocked by any async callbacks + match self.poll_async_task().unwrap_or(Poll::Ready(Ok(()))) { + Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), + Poll::Pending => return Poll::Pending, + Poll::Ready(Ok(_)) => {} + }; - negotiate(self).map_ok(|_| ()) + match negotiate(self) { + Poll::Ready(res) => return Poll::Ready(res.map(|_| ())), + Poll::Pending => { + // If `negotiate` returned `Pending` it could be blocked on a connection future + // (i.e. not socket IO) so before we return, we need to make sure we poll + // the associated future at least once. Otherwise, we will violate the waker contract. + // + // See https://github.com/aws/s2n-quic/pull/2248 + if self.context_mut().async_callback.is_some() { + // continuing in the loop will poll the task + continue; + } + + // we don't have anything else to poll so return `Pending` + return Poll::Pending; + } + } + } } /// Performs the TLS handshake to completion diff --git a/bindings/rust/s2n-tls/src/testing.rs b/bindings/rust/s2n-tls/src/testing.rs index 5ba35b3c33f..55d7a2c0c25 100644 --- a/bindings/rust/s2n-tls/src/testing.rs +++ b/bindings/rust/s2n-tls/src/testing.rs @@ -299,7 +299,8 @@ impl TestPair { (_, Poll::Ready(Err(e))) => return Err(e), // or an error on the client, then return the error (Poll::Ready(Err(e)), _) => return Err(e), - _ => { /* not ready, poll again */ } + // not ready, poll again + _ => {} } } } From 3ef0e32dfe540137a4c0abf5349141bdfb802f20 Mon Sep 17 00:00:00 2001 From: maddeleine <59030281+maddeleine@users.noreply.github.com> Date: Tue, 6 Aug 2024 14:33:30 -0700 Subject: [PATCH 08/19] chore: Bump rust bindings to 0.2.11 (#4690) --- bindings/rust/s2n-tls-hyper/Cargo.toml | 4 ++-- bindings/rust/s2n-tls-sys/templates/Cargo.template | 2 +- bindings/rust/s2n-tls-tokio/Cargo.toml | 4 ++-- bindings/rust/s2n-tls/Cargo.toml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bindings/rust/s2n-tls-hyper/Cargo.toml b/bindings/rust/s2n-tls-hyper/Cargo.toml index bba1ec7b885..3a1b019a939 100644 --- a/bindings/rust/s2n-tls-hyper/Cargo.toml +++ b/bindings/rust/s2n-tls-hyper/Cargo.toml @@ -13,8 +13,8 @@ publish = false default = [] [dependencies] -s2n-tls = { version = "=0.2.10", path = "../s2n-tls" } -s2n-tls-tokio = { version = "=0.2.10", path = "../s2n-tls-tokio" } +s2n-tls = { version = "=0.2.11", path = "../s2n-tls" } +s2n-tls-tokio = { version = "=0.2.11", path = "../s2n-tls-tokio" } hyper = { version = "1" } hyper-util = { version = "0.1", features = ["client-legacy", "tokio", "http1"] } tower-service = { version = "0.3" } diff --git a/bindings/rust/s2n-tls-sys/templates/Cargo.template b/bindings/rust/s2n-tls-sys/templates/Cargo.template index 37ae6bfc870..be03be8426c 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.10" +version = "0.2.11" 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 d7fd23d96b5..b593e008f5b 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.10" +version = "0.2.11" 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.10", path = "../s2n-tls" } +s2n-tls = { version = "=0.2.11", 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 ce70bbf6de1..c5c84c5859c 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.10" +version = "0.2.11" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" @@ -21,7 +21,7 @@ unstable-testing = [] [dependencies] errno = { version = "0.3" } libc = "0.2" -s2n-tls-sys = { version = "=0.2.10", path = "../s2n-tls-sys", features = ["internal"] } +s2n-tls-sys = { version = "=0.2.11", path = "../s2n-tls-sys", features = ["internal"] } pin-project-lite = "0.2" hex = "0.4" From 87deea1928f3eb95464dcb5f1e0a1a24108ae67c Mon Sep 17 00:00:00 2001 From: maddeleine <59030281+maddeleine@users.noreply.github.com> Date: Wed, 7 Aug 2024 10:10:22 -0700 Subject: [PATCH 09/19] feat: Changes ticket encryption scheme to be nonce-reuse resistant (#4663) --- bindings/rust/s2n-tls/src/config.rs | 8 ++ tests/unit/s2n_resume_test.c | 126 ++++++++++++++++++--------- tests/unit/s2n_session_ticket_test.c | 44 +++++++--- tls/s2n_resume.c | 61 ++++++++++++- tls/s2n_resume.h | 15 +++- tls/s2n_server_new_session_ticket.c | 2 +- 6 files changed, 197 insertions(+), 59 deletions(-) diff --git a/bindings/rust/s2n-tls/src/config.rs b/bindings/rust/s2n-tls/src/config.rs index 59e33175915..c7e6353347e 100644 --- a/bindings/rust/s2n-tls/src/config.rs +++ b/bindings/rust/s2n-tls/src/config.rs @@ -754,6 +754,14 @@ impl Builder { Ok(self) } + /// Requires that session tickets are only used when forward secrecy is possible + pub fn require_ticket_forward_secrecy(&mut self, enable: bool) -> Result<&mut Self, Error> { + unsafe { + s2n_config_require_ticket_forward_secrecy(self.as_mut_ptr(), enable).into_result() + }?; + Ok(self) + } + pub fn build(mut self) -> Result { if self.load_system_certs { unsafe { diff --git a/tests/unit/s2n_resume_test.c b/tests/unit/s2n_resume_test.c index c2dfc70baaf..b0c10223fbb 100644 --- a/tests/unit/s2n_resume_test.c +++ b/tests/unit/s2n_resume_test.c @@ -1291,13 +1291,8 @@ int main(int argc, char **argv) }; }; - /* s2n_resume_encrypt_session_ticket */ + /* s2n_resume_encrypt/decrypt_session_ticket */ { - /* Session ticket keys. Taken from test vectors in https://tools.ietf.org/html/rfc5869 */ - uint8_t ticket_key_name[16] = "2016.07.26.15\0"; - S2N_BLOB_FROM_HEX(ticket_key, - "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"); - /* Check error is thrown when no ticket key is available */ { DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), @@ -1312,16 +1307,11 @@ int main(int argc, char **argv) DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); EXPECT_NOT_NULL(config); - /* Adds a valid ticket encryption key */ - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - uint64_t current_time = 0; - EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), - ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); EXPECT_NOT_NULL(conn); + + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); struct s2n_stuffer output = { 0 }; @@ -1332,15 +1322,10 @@ int main(int argc, char **argv) { struct s2n_connection *conn = NULL; struct s2n_config *config = NULL; - uint64_t current_time = 0; EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), - ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); - + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); conn->actual_protocol_version = S2N_TLS12; conn->handshake.handshake_type = NEGOTIATED; @@ -1372,16 +1357,10 @@ int main(int argc, char **argv) { struct s2n_connection *conn = NULL; struct s2n_config *config = NULL; - uint64_t current_time = 0; EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); EXPECT_NOT_NULL(config = s2n_config_new()); - /* Setting up session resumption encryption key */ - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), - ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); - + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); conn->actual_protocol_version = S2N_TLS13; @@ -1414,16 +1393,10 @@ int main(int argc, char **argv) { struct s2n_connection *conn = NULL; struct s2n_config *config = NULL; - uint64_t current_time = 0; EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); EXPECT_NOT_NULL(config = s2n_config_new()); - /* Setting up session resumption encryption key */ - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), - ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); - + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); conn->actual_protocol_version = S2N_TLS13; @@ -1452,6 +1425,82 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_config_free(config)); }; + /* Check error is thrown when wrong version number is read */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS12; + conn->handshake.handshake_type = NEGOTIATED; + + EXPECT_OK(s2n_resume_encrypt_session_ticket(conn, &conn->client_ticket_to_decrypt)); + EXPECT_NOT_EQUAL(s2n_stuffer_data_available(&conn->client_ticket_to_decrypt), 0); + + /* Modify the version number of the ticket */ + uint8_t *version_num = conn->client_ticket_to_decrypt.blob.data; + EXPECT_EQUAL(*version_num, S2N_PRE_ENCRYPTED_STATE_V1); + *version_num = S2N_PRE_ENCRYPTED_STATE_V1 + 100; + + EXPECT_ERROR_WITH_ERRNO(s2n_resume_decrypt_session_ticket(conn, &conn->client_ticket_to_decrypt), + S2N_ERR_SAFETY); + + /* The correct version number should succeed */ + *version_num = S2N_PRE_ENCRYPTED_STATE_V1; + EXPECT_SUCCESS(s2n_stuffer_reread(&conn->client_ticket_to_decrypt)); + EXPECT_OK(s2n_resume_decrypt_session_ticket(conn, &conn->client_ticket_to_decrypt)); + } + + /* Check error is thrown when info bytes used to generate the ticket key are incorrect */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS12; + conn->handshake.handshake_type = NEGOTIATED; + + DEFER_CLEANUP(struct s2n_stuffer valid_ticket = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&valid_ticket, 0)); + EXPECT_OK(s2n_resume_encrypt_session_ticket(conn, &valid_ticket)); + uint32_t ticket_size = s2n_stuffer_data_available(&valid_ticket); + + /* Copy ticket so that we don't modify the original ticket */ + EXPECT_SUCCESS(s2n_stuffer_copy(&valid_ticket, &conn->client_ticket_to_decrypt, + ticket_size)); + + /* We assert that everything up to the info bytes is as expected since this test checks + * a failure condition. This will cause this test to fail earlier if we change the + * serialization format in the future. */ + uint8_t version_number = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&conn->client_ticket_to_decrypt, &version_number)); + EXPECT_EQUAL(version_number, S2N_PRE_ENCRYPTED_STATE_V1); + uint8_t key_name[S2N_TICKET_KEY_NAME_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&conn->client_ticket_to_decrypt, key_name, sizeof(key_name))); + EXPECT_BYTEARRAY_EQUAL(key_name, "2016.07.26.15\0\0", S2N_TICKET_KEY_NAME_LEN); + uint8_t *info_ptr = s2n_stuffer_raw_read(&conn->client_ticket_to_decrypt, S2N_TICKET_INFO_SIZE); + EXPECT_NOT_NULL(info_ptr); + + /* Zero out the info bytes on the ticket.*/ + memset(info_ptr, 0, S2N_TICKET_INFO_SIZE); + EXPECT_SUCCESS(s2n_stuffer_reread(&conn->client_ticket_to_decrypt)); + + EXPECT_ERROR_WITH_ERRNO(s2n_resume_decrypt_session_ticket(conn, &conn->client_ticket_to_decrypt), + S2N_ERR_DECRYPT); + + /* The correct info bytes should succeed */ + EXPECT_SUCCESS(s2n_stuffer_reread(&valid_ticket)); + EXPECT_OK(s2n_resume_decrypt_session_ticket(conn, &valid_ticket)); + } + /* Check session ticket can never be encrypted with a zero-filled ticket key */ { DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); @@ -1460,10 +1509,7 @@ int main(int argc, char **argv) DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); EXPECT_NOT_NULL(config); - /* Add a valid ticket key to the store */ - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), - ticket_key.data, ticket_key.size, 0)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); /* Manually zero out key bytes */ @@ -1484,13 +1530,9 @@ int main(int argc, char **argv) const char test_app_proto[] = "https"; /* Setting up session resumption encryption key */ - uint64_t current_time = 0; struct s2n_config *config = s2n_config_new(); EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), - ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); EXPECT_NOT_NULL(conn); diff --git a/tests/unit/s2n_session_ticket_test.c b/tests/unit/s2n_session_ticket_test.c index 5ec935e4f94..dde2c244e78 100644 --- a/tests/unit/s2n_session_ticket_test.c +++ b/tests/unit/s2n_session_ticket_test.c @@ -25,6 +25,7 @@ #define S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS (S2N_TICKET_ENCRYPT_DECRYPT_KEY_LIFETIME_IN_NANOS + S2N_TICKET_DECRYPT_KEY_LIFETIME_IN_NANOS) / ONE_SEC_IN_NANOS #define S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES S2N_STATE_FORMAT_LEN + S2N_SESSION_TICKET_SIZE_LEN +#define S2N_TICKET_KEY_NAME_LOCATION S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TICKET_VERSION_SIZE #define ONE_SEC_DELAY 1 #define S2N_CLOCK_SYS CLOCK_REALTIME @@ -229,7 +230,8 @@ int main(int argc, char **argv) /* Verify that the client received NST */ serialized_session_state_length = s2n_connection_get_session_length(client_conn); EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); - EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name1, s2n_array_len(ticket_key_name1)); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_TICKET_KEY_NAME_LOCATION, + ticket_key_name1, s2n_array_len(ticket_key_name1)); /* Verify the lifetime hint from the server */ EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); @@ -393,7 +395,8 @@ int main(int argc, char **argv) EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); /* Verify that the new NST is encrypted using second ST */ - EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name2, s2n_array_len(ticket_key_name2)); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_TICKET_KEY_NAME_LOCATION, + ticket_key_name2, s2n_array_len(ticket_key_name2)); EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); @@ -504,7 +507,8 @@ int main(int argc, char **argv) /* Verify that the client received NST */ serialized_session_state_length = s2n_connection_get_session_length(client_conn); EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); - EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name1, s2n_array_len(ticket_key_name1)); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_TICKET_KEY_NAME_LOCATION, + ticket_key_name1, s2n_array_len(ticket_key_name1)); /* Verify the lifetime hint from the server */ EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); @@ -574,7 +578,8 @@ int main(int argc, char **argv) /* Verify that the client received NST */ serialized_session_state_length = s2n_connection_get_session_length(client_conn); EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); - EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name2, s2n_array_len(ticket_key_name2)); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_TICKET_KEY_NAME_LOCATION, + ticket_key_name2, s2n_array_len(ticket_key_name2)); /* Verify the lifetime hint from the server */ EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); @@ -640,7 +645,8 @@ int main(int argc, char **argv) /* Verify that the client received NST */ serialized_session_state_length = s2n_connection_get_session_length(client_conn); EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); - EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name1, s2n_array_len(ticket_key_name1)); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_TICKET_KEY_NAME_LOCATION, + ticket_key_name1, s2n_array_len(ticket_key_name1)); /* Verify the lifetime hint from the server */ EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); @@ -686,7 +692,9 @@ int main(int argc, char **argv) /* Verify that client_ticket is empty */ EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), 1 + 1 + client_conn->session_id_len + S2N_TLS12_STATE_SIZE_IN_BYTES); EXPECT_EQUAL(memcmp(serialized_session_state, &s2n_state_with_session_id, 1), 0); - EXPECT_NOT_EQUAL(memcmp(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name2, s2n_array_len(ticket_key_name2)), 0); + EXPECT_NOT_EQUAL(memcmp(serialized_session_state + S2N_TICKET_KEY_NAME_LOCATION, + ticket_key_name2, s2n_array_len(ticket_key_name2)), + 0); /* Verify the lifetime hint from the server */ EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_ERR_SESSION_TICKET_NOT_SUPPORTED); @@ -809,7 +817,8 @@ int main(int argc, char **argv) /* Verify that the client received NST which is encrypted using a key which is at it's peak encryption */ serialized_session_state_length = s2n_connection_get_session_length(client_conn); EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); - EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name1, s2n_array_len(ticket_key_name1)); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_TICKET_KEY_NAME_LOCATION, + ticket_key_name1, s2n_array_len(ticket_key_name1)); /* Verify the lifetime hint from the server */ EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); @@ -893,7 +902,8 @@ int main(int argc, char **argv) serialized_session_state_length = s2n_connection_get_session_length(client_conn); EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); - int result = memcmp(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name2, + int result = memcmp(serialized_session_state + S2N_TICKET_KEY_NAME_LOCATION, + ticket_key_name2, s2n_array_len(ticket_key_name2)); if (result == 0) { expected_key_chosen = true; @@ -967,7 +977,8 @@ int main(int argc, char **argv) /* Verify that the client received NST which is encrypted using a key which is at it's peak encryption */ serialized_session_state_length = s2n_connection_get_session_length(client_conn); EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); - EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name2, s2n_array_len(ticket_key_name2)); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_TICKET_KEY_NAME_LOCATION, + ticket_key_name2, s2n_array_len(ticket_key_name2)); /* Verify the lifetime hint from the server */ EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), 86400 + 18000); @@ -1027,7 +1038,8 @@ int main(int argc, char **argv) /* Verify that the client received NST which is encrypted using a key which is at it's peak encryption */ serialized_session_state_length = s2n_connection_get_session_length(client_conn); EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); - EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name2, s2n_array_len(ticket_key_name2)); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_TICKET_KEY_NAME_LOCATION, + ticket_key_name2, s2n_array_len(ticket_key_name2)); /* Verify that the keys are stored from oldest to newest */ EXPECT_OK(s2n_set_get(server_config->ticket_keys, 0, (void **) &ticket_key)); @@ -1097,9 +1109,13 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - /* Setup stuffers value containing the valid key name, valid iv and invalid encrypted blob */ + /* Setup stuffers value containing the valid version number, valid key name, valid info, valid iv and invalid encrypted blob */ + POSIX_GUARD(s2n_stuffer_write_uint8(&server_conn->client_ticket_to_decrypt, S2N_PRE_ENCRYPTED_STATE_V1)); POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, ticket_key_name1, s2n_array_len(ticket_key_name1))); + uint8_t valid_info[S2N_TICKET_INFO_SIZE] = { 0 }; + POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, valid_info, sizeof(valid_info))); + uint8_t valid_iv[S2N_TLS_GCM_IV_LEN] = { 0 }; POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, valid_iv, sizeof(valid_iv))); @@ -1123,9 +1139,13 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - /* Setup stuffers value containing the invalid key name, valid iv and invalid encrypted blob */ + /* Setup stuffers value containing the valid version number, invalid key name, valid iv, valid info, and invalid encrypted blob */ + POSIX_GUARD(s2n_stuffer_write_uint8(&server_conn->client_ticket_to_decrypt, S2N_PRE_ENCRYPTED_STATE_V1)); POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, ticket_key_name2, s2n_array_len(ticket_key_name2))); + uint8_t valid_info[S2N_TICKET_INFO_SIZE] = { 0 }; + POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, valid_info, sizeof(valid_info))); + uint8_t valid_iv[S2N_TLS_GCM_IV_LEN] = { 0 }; POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, valid_iv, sizeof(valid_iv))); diff --git a/tls/s2n_resume.c b/tls/s2n_resume.c index 1bccc857b2c..c6e0e32af4d 100644 --- a/tls/s2n_resume.c +++ b/tls/s2n_resume.c @@ -782,6 +782,39 @@ struct s2n_ticket_key *s2n_find_ticket_key(struct s2n_config *config, const uint return NULL; } +struct s2n_unique_ticket_key { + struct s2n_blob initial_key; + uint8_t info[S2N_AES256_KEY_LEN]; + uint8_t output_key[S2N_AES256_KEY_LEN]; +}; + +/* Ensures that a session ticket encryption key is used only once per ticket. + * + * The AES-GCM encryption scheme breaks if the same nonce is used with the same key more than once. + * As the number of TLS connections increases per second, it becomes more probable that the same + * random nonce will be generated twice and used with the same ticket key. + * To avoid this we generate a unique session ticket encryption key for each ticket. + **/ +static S2N_RESULT s2n_resume_generate_unique_ticket_key(struct s2n_unique_ticket_key *key) +{ + RESULT_ENSURE_REF(key); + + struct s2n_blob out_key_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&out_key_blob, key->output_key, sizeof(key->output_key))); + struct s2n_blob info_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&info_blob, key->info, sizeof(key->info))); + struct s2n_blob salt = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&salt, NULL, 0)); + + DEFER_CLEANUP(struct s2n_hmac_state hmac = { 0 }, s2n_hmac_free); + /* TODO: There may be an optimization here to reuse existing hmac memory instead of + * creating an entirely new hmac. See: https://github.com/aws/s2n-tls/issues/3206 */ + RESULT_GUARD_POSIX(s2n_hmac_new(&hmac)); + RESULT_GUARD_POSIX(s2n_hkdf(&hmac, S2N_HMAC_SHA256, &salt, &key->initial_key, &info_blob, &out_key_blob)); + + return S2N_RESULT_OK; +} + S2N_RESULT s2n_resume_encrypt_session_ticket(struct s2n_connection *conn, struct s2n_stuffer *to) { RESULT_ENSURE_REF(conn); @@ -791,9 +824,17 @@ S2N_RESULT s2n_resume_encrypt_session_ticket(struct s2n_connection *conn, struct /* No keys loaded by the user or the keys are either in decrypt-only or expired state */ RESULT_ENSURE(key != NULL, S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); + /* Generate unique per-ticket encryption key */ + struct s2n_unique_ticket_key ticket_key = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&ticket_key.initial_key, key->aes_key, sizeof(key->aes_key))); + struct s2n_blob info_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&info_blob, ticket_key.info, sizeof(ticket_key.info))); + RESULT_GUARD(s2n_get_public_random_data(&info_blob)); + RESULT_GUARD(s2n_resume_generate_unique_ticket_key(&ticket_key)); + /* Initialize AES key */ struct s2n_blob aes_key_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&aes_key_blob, key->aes_key, sizeof(key->aes_key))); + RESULT_GUARD_POSIX(s2n_blob_init(&aes_key_blob, ticket_key.output_key, sizeof(ticket_key.output_key))); DEFER_CLEANUP(struct s2n_session_key aes_ticket_key = { 0 }, s2n_session_key_free); RESULT_GUARD_POSIX(s2n_session_key_alloc(&aes_ticket_key)); RESULT_GUARD(s2n_aes256_gcm.init(&aes_ticket_key)); @@ -813,9 +854,15 @@ S2N_RESULT s2n_resume_encrypt_session_ticket(struct s2n_connection *conn, struct RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&aad, key->implicit_aad, sizeof(key->implicit_aad))); RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&aad, key->key_name, sizeof(key->key_name))); + /* Write version number */ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(to, S2N_PRE_ENCRYPTED_STATE_V1)); + /* Write key name */ RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(to, key->key_name, sizeof(key->key_name))); + /* Write parameter needed to generate unique ticket key */ + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(to, ticket_key.info, sizeof(ticket_key.info))); + /* Write IV */ uint8_t iv_data[S2N_TLS_GCM_IV_LEN] = { 0 }; struct s2n_blob iv = { 0 }; @@ -850,6 +897,11 @@ static S2N_RESULT s2n_resume_decrypt_session(struct s2n_connection *conn, struct RESULT_ENSURE_REF(conn->config); RESULT_ENSURE_REF(key_intro_time); + /* Read version number */ + uint8_t version = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &version)); + RESULT_ENSURE_EQ(version, S2N_PRE_ENCRYPTED_STATE_V1); + /* Read key name */ uint8_t key_name[S2N_TICKET_KEY_NAME_LEN] = { 0 }; RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, key_name, sizeof(key_name))); @@ -858,6 +910,11 @@ static S2N_RESULT s2n_resume_decrypt_session(struct s2n_connection *conn, struct /* Key has expired; do full handshake */ RESULT_ENSURE(key != NULL, S2N_ERR_KEY_USED_IN_SESSION_TICKET_NOT_FOUND); + struct s2n_unique_ticket_key ticket_key = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&ticket_key.initial_key, key->aes_key, sizeof(key->aes_key))); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, ticket_key.info, sizeof(ticket_key.info))); + RESULT_GUARD(s2n_resume_generate_unique_ticket_key(&ticket_key)); + /* Read IV */ uint8_t iv_data[S2N_TLS_GCM_IV_LEN] = { 0 }; struct s2n_blob iv = { 0 }; @@ -866,7 +923,7 @@ static S2N_RESULT s2n_resume_decrypt_session(struct s2n_connection *conn, struct /* Initialize AES key */ struct s2n_blob aes_key_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&aes_key_blob, key->aes_key, sizeof(key->aes_key))); + RESULT_GUARD_POSIX(s2n_blob_init(&aes_key_blob, ticket_key.output_key, sizeof(ticket_key.output_key))); DEFER_CLEANUP(struct s2n_session_key aes_ticket_key = { 0 }, s2n_session_key_free); RESULT_GUARD_POSIX(s2n_session_key_alloc(&aes_ticket_key)); RESULT_GUARD(s2n_aes256_gcm.init(&aes_ticket_key)); diff --git a/tls/s2n_resume.h b/tls/s2n_resume.h index 36b06de1f14..2a65e651fc5 100644 --- a/tls/s2n_resume.h +++ b/tls/s2n_resume.h @@ -35,8 +35,10 @@ #define ONE_SEC_IN_NANOS 1000000000 #define ONE_MILLISEC_IN_NANOS 1000000 #define ONE_WEEK_IN_SEC 604800 -#define S2N_TLS12_TICKET_SIZE_IN_BYTES (S2N_TICKET_KEY_NAME_LEN + S2N_TLS_GCM_IV_LEN \ - + S2N_TLS12_STATE_SIZE_IN_BYTES + S2N_TLS_GCM_TAG_LEN) +#define S2N_TICKET_INFO_SIZE 32 +#define S2N_TICKET_VERSION_SIZE 1 +#define S2N_TLS12_TICKET_SIZE_IN_BYTES (S2N_TICKET_VERSION_SIZE + S2N_TICKET_KEY_NAME_LEN \ + + S2N_TICKET_INFO_SIZE + S2N_TLS_GCM_IV_LEN + S2N_TLS12_STATE_SIZE_IN_BYTES + S2N_TLS_GCM_TAG_LEN) #define S2N_TICKET_ENCRYPT_DECRYPT_KEY_LIFETIME_IN_NANOS 7200000000000 /* 2 hours */ #define S2N_TICKET_DECRYPT_KEY_LIFETIME_IN_NANOS 46800000000000 /* 13 hours */ @@ -95,6 +97,15 @@ typedef enum { S2N_SERIALIZED_FORMAT_TLS12_V3, } s2n_serial_format_version; +/* Used to specify the format of the ticket schema before encryption. + * + * This makes it easier to make changes to the ticket schema in the future + * as it allows us to interpret and parse all ticket schemas. + **/ +typedef enum { + S2N_PRE_ENCRYPTED_STATE_V1 = 1, +} s2n_pre_encrypted_state; + int s2n_allowed_to_cache_connection(struct s2n_connection *conn); int s2n_resume_from_cache(struct s2n_connection *conn); S2N_RESULT s2n_store_to_cache(struct s2n_connection *conn); diff --git a/tls/s2n_server_new_session_ticket.c b/tls/s2n_server_new_session_ticket.c index 1c7bb404cec..f0cb82ab473 100644 --- a/tls/s2n_server_new_session_ticket.c +++ b/tls/s2n_server_new_session_ticket.c @@ -37,7 +37,7 @@ * * This constant is enforced via unit tests. */ -#define S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE 79 +#define S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE 112 int s2n_server_nst_recv(struct s2n_connection *conn) { From 1511c517f8c9a0c76fbf128196be74663991e3c5 Mon Sep 17 00:00:00 2001 From: Jou Ho <43765840+jouho@users.noreply.github.com> Date: Wed, 7 Aug 2024 14:37:59 -0700 Subject: [PATCH 10/19] ci: store fuzz artifacts in s3 (#4678) --- tests/fuzz/Makefile | 6 +++++- tests/fuzz/runFuzzTest.sh | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/fuzz/Makefile b/tests/fuzz/Makefile index b2bd95dba32..d702ba39326 100644 --- a/tests/fuzz/Makefile +++ b/tests/fuzz/Makefile @@ -32,6 +32,10 @@ ifndef CORPUS_UPLOAD_LOC export CORPUS_UPLOAD_LOC="none" endif +ifndef ARTIFACT_UPLOAD_LOC + export ARTIFACT_UPLOAD_LOC="none" +endif + ifndef FUZZ_TESTS export FUZZ_TESTS=${TESTS} endif @@ -72,7 +76,7 @@ run_tests:: $(FUZZ_TESTS) ld-preload export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}; \ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}; \ export LIBCRYPTO_ROOT=${LIBCRYPTO_ROOT}; \ - ./runFuzzTest.sh $${test_name} ${FUZZ_TIMEOUT_SEC} ${CORPUS_UPLOAD_LOC}; done; \ + ./runFuzzTest.sh $${test_name} ${FUZZ_TIMEOUT_SEC} ${CORPUS_UPLOAD_LOC} ${ARTIFACT_UPLOAD_LOC}; done; \ } ./calcTotalCov.sh diff --git a/tests/fuzz/runFuzzTest.sh b/tests/fuzz/runFuzzTest.sh index 17194030f19..a0ac257e6d9 100755 --- a/tests/fuzz/runFuzzTest.sh +++ b/tests/fuzz/runFuzzTest.sh @@ -22,13 +22,14 @@ usage() { exit 1 } -if [ "$#" -ne "3" ]; then +if [ "$#" -ne "4" ]; then usage fi TEST_NAME=$1 FUZZ_TIMEOUT_SEC=$2 CORPUS_UPLOAD_LOC=$3 +ARTIFACT_UPLOAD_LOC=$4 MIN_TEST_PER_SEC="1000" MIN_FEATURES_COVERED="100" @@ -201,6 +202,24 @@ then if [ $EXPECTED_TEST_FAILURE == 1 ]; then + # Store corpus to S3 to be used for debugging if the test is negative + unset LD_PRELOAD + unset LD_LIBRARY_PATH + if [ "$CORPUS_UPLOAD_LOC" != "none" ]; then + printf "Zipping corpus files...\n" + zip -r ./corpus/${TEST_NAME}.zip ./corpus/${TEST_NAME}/ + + printf "Uploading zipped corpus file to S3 bucket...\n" + aws s3 cp ./corpus/${TEST_NAME}.zip $CORPUS_UPLOAD_LOC/${TEST_NAME}/corpus_$(date +%Y-%m-%d-%T).zip + fi + + # Store generated output files in the S3 bucket. + if [ "$ARTIFACT_UPLOAD_LOC" != "none" ]; then + printf "Uploading output files to S3 bucket...\n" + aws s3 cp ./${TEST_NAME}_output.txt ${ARTIFACT_UPLOAD_LOC}/${TEST_NAME}/output_$(date +%Y-%m-%d-%T).txt + aws s3 cp ./${TEST_NAME}_results.txt ${ARTIFACT_UPLOAD_LOC}/${TEST_NAME}/results_$(date +%Y-%m-%d-%T).txt + fi + # Clean up LibFuzzer corpus files if the test is negative. printf "\n" rm -f leak-* crash-* From f6a5c2fbaa1315b0ba03454c6be4b23c633f2307 Mon Sep 17 00:00:00 2001 From: Jou Ho <43765840+jouho@users.noreply.github.com> Date: Wed, 7 Aug 2024 18:38:33 -0700 Subject: [PATCH 11/19] chore: document OpenSSL-FIPS restriction on RSA key size (#4654) --- crypto/s2n_rsa.c | 2 ++ tests/testlib/s2n_testlib.h | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/crypto/s2n_rsa.c b/crypto/s2n_rsa.c index 2c5219ec86e..0274284a164 100644 --- a/crypto/s2n_rsa.c +++ b/crypto/s2n_rsa.c @@ -120,6 +120,7 @@ static int s2n_rsa_encrypt(const struct s2n_pkey *pub, struct s2n_blob *in, stru /* Safety: RSA_public_encrypt does not mutate the key */ int r = RSA_public_encrypt(in->size, (unsigned char *) in->data, (unsigned char *) out->data, s2n_unsafe_rsa_get_non_const(pub_key), RSA_PKCS1_PADDING); + POSIX_ENSURE(r >= 0, S2N_ERR_ENCRYPT); POSIX_ENSURE((int64_t) r == (int64_t) out->size, S2N_ERR_SIZE_MISMATCH); return 0; @@ -142,6 +143,7 @@ static int s2n_rsa_decrypt(const struct s2n_pkey *priv, struct s2n_blob *in, str /* Safety: RSA_private_decrypt does not mutate the key */ int r = RSA_private_decrypt(in->size, (unsigned char *) in->data, intermediate, s2n_unsafe_rsa_get_non_const(priv_key), RSA_NO_PADDING); + POSIX_ENSURE(r >= 0, S2N_ERR_DECRYPT); POSIX_ENSURE((int64_t) r == (int64_t) expected_size, S2N_ERR_SIZE_MISMATCH); s2n_constant_time_pkcs1_unpad_or_dont(out->data, intermediate, r, out->size); diff --git a/tests/testlib/s2n_testlib.h b/tests/testlib/s2n_testlib.h index cc9d5614035..60effca24de 100644 --- a/tests/testlib/s2n_testlib.h +++ b/tests/testlib/s2n_testlib.h @@ -209,7 +209,10 @@ int s2n_test_cert_chain_and_key_new(struct s2n_cert_chain_and_key **chain_and_ke * cert with `p256` or `p384` * @param digest indicates the certificate signature digest of `sha256` or * `sha384` -*/ + * + * @note The OpenSSL FIPS module requires the key size for RSA certs to be greater than or equal to 1024 bits. + * See https://github.com/aws/s2n-tls/issues/4651 for more information. + */ int s2n_test_cert_permutation_load_server_chain(struct s2n_cert_chain_and_key **chain_and_key, const char *type, const char *siganture, const char *size, const char *digest); From bb3509cd1d256805d15fb0c095a3276246997f2c Mon Sep 17 00:00:00 2001 From: kaukabrizvi <100529019+kaukabrizvi@users.noreply.github.com> Date: Thu, 8 Aug 2024 08:55:53 -0700 Subject: [PATCH 12/19] Enabling differential performance benchmarking (#4667) --- tests/regression/Cargo.toml | 2 + tests/regression/README.md | 109 +++++++---- tests/regression/src/lib.rs | 374 ++++++++++++++++++++++++++---------- 3 files changed, 339 insertions(+), 146 deletions(-) diff --git a/tests/regression/Cargo.toml b/tests/regression/Cargo.toml index d8f07d9ab63..051953af148 100644 --- a/tests/regression/Cargo.toml +++ b/tests/regression/Cargo.toml @@ -10,5 +10,7 @@ errno = { version = "0.3" } libc = "0.2" crabgrind = "0.1" futures-test = "0.3.30" +glob = "0.3.1" + [profile.release] debug = true diff --git a/tests/regression/README.md b/tests/regression/README.md index f79c0964da9..652d73cab39 100644 --- a/tests/regression/README.md +++ b/tests/regression/README.md @@ -1,10 +1,15 @@ # Regression Testing for s2n-tls -This folder contains regression tests and benchmarking tools for the `s2n-tls` library. The tests focus on various aspects of TLS connections. +This folder contains regression tests and benchmarking tools for the `s2n-tls` library. The tests focus on various aspects of TLS connections. + ## Testing Philosophy Currently, s2n-tls implements a wall clock benchmarking tool which measures end-to-end handshake performance to compare s2n-tls with rustls and OpenSSL. In the past, s2n-tls has tried benchmarking to detect regressions through criterion in Rust, but the subprocess and spin-up time contributed to performance measurement which made the results inaccurate and difficult to use in CI. The project has a slightly different focus, learning from these existing tools. Performance assertion in s2n-tls focuses on a benchmarking tool that can detail performance by API path and do so with enough repeatability and accuracy to detect regressions between two versions of s2n-tls so that performance analysis can occur at PR time. This means that the scope of each harness is limited and mutually exclusive of other harnesses since we are intersted in measuring the performance of the important paths a TLS connection typically follows. + +### Why CPU instructions +The performance benchmarking framework utilizes CPU Instruction count across API paths to make the regression assertion. This technique reduces noise, ensuring that small performance differences are caught through this measure. While a difference in performance count may not result in a direct runtime difference, it is useful when comparing a PR to mainline and to dig into the specific sources of performance impact within the code. + ## Contents 1. **lib.rs** @@ -25,71 +30,95 @@ Ensure you have the following installed: To run the harnesses with Valgrind and store the annotated results, run: ``` -ENABLE_VALGRIND = true cargo test +PERF_MODE=valgrind cargo test +``` + +This will recursively call all tests with valgrind enabled so the performance output is generated and stored in target/perf_outputs. If you are looking for the scalar performance output of a PR, this will provide inisght into which portions of the code account for what share of the CPU instruction count. + +## Running the Harnesses between versions (differential performance) +Run the scalar performance for all harnesses on the current branch version of s2n-tls +``` +PERF_MODE=valgrind cargo test +``` +`git checkout` or `git switch` to mainline/version being compared to. Make sure you have stashed or committed any changes. +``` +PERF_MODE=valgrind cargo test ``` +`git checkout` or `git switch` back to the original version. At this point you should have two annotated performance outputs for each test. If you have more, the diff test will not be able to recognize the versions being compared. +``` +PERF_MODE=diff cargo test +``` +This will assert on the performance difference of the current version minus the previous. If the regression exceeds the const `MAX_DIFF`, the test fails. Performance output profiles are stored by their commit id in `/target/commit_id`: +- `raw_profile` for the unannotated cachegrind output result +- `annotated_profile` for the annotated cachegrind output (scalar) +- `target/diff` contains the annotated differential profile between two commits -This will recursively call all tests with valgrind enabled so the performance output is generated and stored ## Running the tests w/o Valgrind ``` cargo test ``` -This will run the tests without valgrind to test if the process completes as expected -## Sample Output for Valgrind test +This will run the tests without valgrind to test if the hanresses complete as expected + +## Output Files +- `target/$commit_id/test_name.raw`: Contains the raw cachegrind profile. On its own, the file is pretty much unreadable but is useful for the cg_annotate --diff functionality or to visualize the profile via tools like [KCachegrind](https://kcachegrind.github.io/html/Home.html). +- `target/$commit_id/test_name.annotated`: The scalar annotated profile associated with that particular commit id. This file contains detailed infromation on the contribution of functions, files, and lines of code to the overall scalar performance count. +- `target/diff/test_name.diff`: The annotated performance difference between two commits. This file contains the overall performance difference and also details the instruction counts, how many instructions a particular file/function account for, and the contribution of individual lines of code to the overall instruction count difference. -Running the test will run all harnesses and fail if any number of harnesses exceed the performance threshold. For example, a regression test faliure could look like: +## Sample Output for Valgrind test (differential) + +Running the differential test will run all harnesses and fail if any number of harnesses exceed the performance threshold. For example, a regression test faliure could look like: ``` ----- tests::test_set_security_policy_and_build stdout ---- -Running command: valgrind --tool=cachegrind --cachegrind-out-file=cachegrind_test_set_security_policy_and_build.out /home/ubuntu/proj/s2n/tests/regression/target/debug/deps/regression-7c7d86aeafe3b426 test_set_security_policy_and_build -Running command: cg_annotate cachegrind_test_set_security_policy_and_build.out > perf_outputs/test_set_security_policy_and_build.annotated.txt -thread 'tests::test_set_security_policy_and_build' panicked at src/lib.rs:174:9: -Instruction count difference in test_set_security_policy_and_build exceeds the threshold, regression of 13975865 instructions -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +running 2 tests +test tests::test_set_config ... FAILED +test tests::test_rsa_handshake ... ok + +failures: ----- tests::test_rsa_handshake stdout ---- -Running command: valgrind --tool=cachegrind --cachegrind-out-file=cachegrind_test_rsa_handshake.out /home/ubuntu/proj/s2n/tests/regression/target/debug/deps/regression-7c7d86aeafe3b426 test_rsa_handshake -Running command: cg_annotate cachegrind_test_rsa_handshake.out > perf_outputs/test_rsa_handshake.annotated.txt -thread 'tests::test_rsa_handshake' panicked at src/lib.rs:174:9: -Instruction count difference in test_rsa_handshake exceeds the threshold, regression of 51176459 instructions +---- tests::test_set_config stdout ---- +Instruction difference for test_set_config: 245746 +thread 'tests::test_set_config' panicked at src/lib.rs:189:9: +Instruction count difference in test_set_config exceeds the threshold, regression of 245746 instructions +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace failures: - tests::test_rsa_handshake - tests::test_set_security_policy_and_build + tests::test_set_config + +test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.20s ``` -It also produces annotated cachegrind files stored in the `perf_ouput` directory which detail the instruction counts, how many instructions a particular file/function account for, and the contribution of individual lines of code to the overall instruction count. For example, these are the first few lines of the output generated for 'test_rsa_handshake.annotated.txt': +### target/diff/test_set_config.diff ``` -------------------------------------------------------------------------------- -- Summary -------------------------------------------------------------------------------- -Ir_________________ +Ir______________ -79,270,744 (100.0%) PROGRAM TOTALS +245,746 (100.0%) PROGRAM TOTALS -------------------------------------------------------------------------------- -- File:function summary -------------------------------------------------------------------------------- - Ir_______________________ file:function - -< 71,798,872 (90.6%, 90.6%) /home/ubuntu/.cargo/registry/src/index.crates.io-6f17d22bba15001f/aws-lc-sys-0.19.0/aws-lc/generated-src/linux-x86_64/crypto/fipsmodule/x86_64-mont5.S: - 54,908,926 (69.3%) aws_lc_0_19_0_bn_sqr8x_internal - 15,699,024 (19.8%) mul4x_internal - 1,114,840 (1.4%) __bn_post4x_internal - -< 1,551,316 (2.0%, 92.5%) /home/ubuntu/.cargo/registry/src/index.crates.io-6f17d22bba15001f/aws-lc-sys-0.19.0/aws-lc/generated-src/linux-x86_64/crypto/fipsmodule/p256-x86_64-asm.S: - 676,336 (0.9%) __ecp_nistz256_mul_montq - 475,750 (0.6%) __ecp_nistz256_sqr_montq - 95,732 (0.1%) aws_lc_0_19_0_ecp_nistz256_point_double - -< 833,553 (1.1%, 93.6%) /home/ubuntu/.cargo/registry/src/index.crates.io-6f17d22bba15001f/aws-lc-sys-0.19.0/aws-lc/generated-src/linux-x86_64/crypto/fipsmodule/sha256-x86_64.S: - 830,671 (1.0%) sha256_block_data_order_avx - -< 557,697 (0.7%, 94.3%) /home/ubuntu/.cargo/registry/src/index.crates.io-6f17d22bba15001f/aws-lc-sys-0.19.0/aws-lc/generated-src/linux-x86_64/crypto/fipsmodule/x86_64-mont.S: - 493,032 (0.6%) bn_mul4x_mont - + Ir______________________ file:function + +< 243,774 (99.2%, 99.2%) /home/ubuntu/.cargo/registry/src/index.crates.io-6f17d22bba15001f/aws-lc-sys-0.19.0/aws-lc/crypto/fipsmodule/../internal.h: + 62,034 (25.2%) constant_time_select_w + 47,264 (19.2%) value_barrier_w + 47,264 (19.2%) constant_time_select_int + 25,279 (10.3%) constant_time_lt_w + 20,692 (8.4%) constant_time_msb_w + 20,566 (8.4%) constant_time_is_zero_w + 16,346 (6.7%) constant_time_eq_w + 2,720 (1.1%) CRYPTO_addc_u64 + 608 (0.2%) OPENSSL_memcpy + -504 (-0.2%) CRYPTO_subc_u64 + 480 (0.2%) CRYPTO_bswap4 + 424 (0.2%) OPENSSL_memset + 315 (0.1%) CRYPTO_load_u32_be + 270 (0.1%) CRYPTO_store_u32_be ``` ### Understanding the Annotated Output diff --git a/tests/regression/src/lib.rs b/tests/regression/src/lib.rs index 6a748b778ee..2794f5f8db5 100644 --- a/tests/regression/src/lib.rs +++ b/tests/regression/src/lib.rs @@ -1,48 +1,61 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use s2n_tls::{ - config::Builder, - security, - testing::{CertKeyPair, InsecureAcceptAllCertificatesHandler}, -}; -type Error = s2n_tls::error::Error; - -/// Function to create default config with specified parameters. -pub fn set_config( - cipher_prefs: &security::Policy, - keypair: CertKeyPair, -) -> Result { - let mut builder = Builder::new(); - builder - .set_security_policy(cipher_prefs) - .expect("Unable to set config cipher preferences"); - builder - .set_verify_host_callback(InsecureAcceptAllCertificatesHandler {}) - .expect("Unable to set a host verify callback."); - builder - .load_pem(keypair.cert(), keypair.key()) - .expect("Unable to load cert/pem"); - builder.trust_pem(keypair.cert()).expect("load cert pem"); - builder.build() +pub mod git { + use std::process::Command; + + pub fn get_current_commit_hash() -> String { + let output = Command::new("git") + .args(["rev-parse", "HEAD"]) + .output() + .expect("Failed to get commit hash"); + assert!(output.status.success()); + + String::from_utf8(output.stdout) + .expect("Invalid UTF-8 in commit hash") + .trim() + .to_string() + } + + /// Returns true if `commit1` is older than `commit2` + pub fn is_older_commit(commit1: &str, commit2: &str) -> bool { + let status = Command::new("git") + .args(["merge-base", "--is-ancestor", commit1, commit2]) + .status() + .expect("Failed to execute git merge-base"); + status.success() + } + + pub fn extract_commit_hash(file: &str) -> String { + // input: "target/$commit_id/test_name.raw" + // output: "$commit_id" + file.split("target/") + .nth(1) + .and_then(|s| s.split('/').next()) + .map(|s| s.to_string()) + .unwrap_or_default() // This will return an empty string if the Option is None + } } #[cfg(test)] mod tests { + use s2n_tls::{ + config::Builder, + security, + testing::{CertKeyPair, InsecureAcceptAllCertificatesHandler}, + }; + type Error = s2n_tls::error::Error; use super::*; use crabgrind as cg; use s2n_tls::testing::TestPair; use std::{ env, - fs::{create_dir_all, File}, - io::{self, BufRead, Write}, - path::Path, - process::Command, + fs::{create_dir_all, write}, + io::{self, BufRead}, + process::{Command, Output}, }; - /// Configurable threshold for regression testing. - /// Tests will fail if the instruction count difference is greater than the value of this constant. - const MAX_DIFF: u64 = 1_000_000; + const MAX_DIFF: i64 = 1_000; struct InstrumentationControl; @@ -55,24 +68,238 @@ mod tests { cg::cachegrind::start_instrumentation(); } } - /// Environment variable to determine whether to run under valgrind or solely test functionality. - fn is_running_under_valgrind() -> bool { - env::var("ENABLE_VALGRIND").is_ok() + + #[derive(Debug)] + enum RegressionTestMode { + Default, + Valgrind, + Diff, + } + + impl RegressionTestMode { + fn from_env() -> Self { + match env::var("PERF_MODE").as_deref() { + Ok("valgrind") => RegressionTestMode::Valgrind, + Ok("diff") => RegressionTestMode::Diff, + _ => RegressionTestMode::Default, + } + } } fn valgrind_test(test_name: &str, test_body: F) -> Result<(), s2n_tls::error::Error> where F: FnOnce(&InstrumentationControl) -> Result<(), s2n_tls::error::Error>, { - if !is_running_under_valgrind() { - let ctrl = InstrumentationControl; - test_body(&ctrl) - } else { - run_valgrind_test(test_name); - Ok(()) + match RegressionTestMode::from_env() { + RegressionTestMode::Valgrind => { + let raw_profile = RawProfile::new(test_name); + AnnotatedProfile::new(&raw_profile); + } + RegressionTestMode::Diff => { + let (prev_profile, curr_profile) = RawProfile::query(test_name); + DiffProfile::new(&prev_profile, &curr_profile).assert_performance(); + } + RegressionTestMode::Default => { + let ctrl = InstrumentationControl; + test_body(&ctrl)? + } + }; + Ok(()) + } + + struct RawProfile { + test_name: String, + commit_hash: String, + } + + impl RawProfile { + fn new(test_name: &str) -> Self { + let commit_hash = git::get_current_commit_hash(); + create_dir_all(format!("target/{commit_hash}")).unwrap(); + + let raw_profile = Self { + test_name: test_name.to_owned(), + commit_hash, + }; + + let mut command = Command::new("valgrind"); + command + .args([ + // use cachegrind to get instruction count + "--tool=cachegrind", + // write output to output file + &format!("--cachegrind-out-file={}", raw_profile.path()), + // the "cargo test" executable + &std::env::args().next().unwrap(), + test_name, + ]) + // remove environment variable to prevent recursive loop + .env_remove("PERF_MODE"); + assert_command_success(command.output().unwrap()); + + raw_profile + } + + fn path(&self) -> String { + format!("target/{}/{}.raw", self.commit_hash, self.test_name) + } + + /// Return the raw profiles for `test_name` in "git" order. `tuple.0` is older than `tuple.1` + /// + /// This method will panic if there are not two profiles. + fn query(test_name: &str) -> (RawProfile, RawProfile) { + let pattern = format!("target/**/*{}.raw", test_name); + let raw_files: Vec = glob::glob(&pattern) + .expect("Failed to read glob pattern") + .filter_map(Result::ok) + .map(|path| path.to_string_lossy().into_owned()) + .collect(); + assert_eq!(raw_files.len(), 2); + + let profile1 = RawProfile { + test_name: test_name.to_string(), + commit_hash: git::extract_commit_hash(&raw_files[0]), + }; + + let profile2 = RawProfile { + test_name: test_name.to_string(), + commit_hash: git::extract_commit_hash(&raw_files[1]), + }; + + if git::is_older_commit(&profile1.commit_hash, &profile2.commit_hash) { + (profile1, profile2) + } else if git::is_older_commit(&profile2.commit_hash, &profile1.commit_hash) { + (profile2, profile1) + } else { + panic!("The commits are not in the same log"); + } + } + } + + struct AnnotatedProfile { + test_name: String, + commit_hash: String, + } + + impl AnnotatedProfile { + fn new(raw_profile: &RawProfile) -> Self { + let annotated = Self { + test_name: raw_profile.test_name.clone(), + commit_hash: raw_profile.commit_hash.clone(), + }; + + // annotate raw profile + let annotate_output = Command::new("cg_annotate") + .arg(raw_profile.path()) + .output() + .expect("Failed to run cg_annotate"); + assert_command_success(annotate_output.clone()); + + // write annotated profile to disk + let annotate_content = String::from_utf8(annotate_output.stdout) + .expect("Invalid UTF-8 in cg_annotate output"); + write(annotated.path(), annotate_content).expect("Failed to write to file"); + + annotated + } + + fn path(&self) -> String { + format!("target/{}/{}.annotated", self.commit_hash, self.test_name) } } + struct DiffProfile { + test_name: String, + } + impl DiffProfile { + fn new(prev_profile: &RawProfile, curr_profile: &RawProfile) -> Self { + let diff_profile = Self { + test_name: curr_profile.test_name.clone(), + }; + + // diff the raw profile + let diff_output = Command::new("cg_annotate") + .args(["--diff", &prev_profile.path(), &curr_profile.path()]) + .output() + .expect("Failed to run cg_annotate --diff"); + assert_command_success(diff_output.clone()); + + // write the diff to disk + let diff_content = String::from_utf8(diff_output.stdout) + .expect("Invalid UTF-8 in cg_annotate --diff output"); + write(diff_profile.path(), diff_content).expect("Failed to write to file"); + + diff_profile + } + + fn path(&self) -> String { + format!("target/diff/{}.diff", self.test_name) + } + + fn assert_performance(&self) { + let diff_content = std::fs::read_to_string(self.path()).unwrap(); + + let diff = find_instruction_count(&diff_content) + .expect("Failed to parse cg_annotate --diff output"); + assert!( + diff <= MAX_DIFF, + "Instruction count difference exceeds the threshold, regression of {} instructions. + Check the annotated output logs in target/diff/{}.diff for debug information", + diff, self.test_name + ); + } + } + + // Pulls the instruction count as an integer from the annotated output file. + fn find_instruction_count(output: &str) -> Result { + let reader = io::BufReader::new(output.as_bytes()); + // Example of the line being parsed: + // "79,278,369 (100.0%) PROGRAM TOTALS" + for line in reader.lines() { + let line = line?; + if line.contains("PROGRAM TOTALS") { + if let Some(instructions) = line.split_whitespace().next() { + return instructions + .replace(',', "") + .parse::() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)); + } + } + } + panic!("Failed to find instruction count in annotated file"); + } + + // Asserts that a command executed successfully, panic with stdout/stderr if it fails + fn assert_command_success(output: Output) { + if !output.status.success() { + let stdout = std::str::from_utf8(&output.stdout).unwrap_or("Failed to read stdout"); + let stderr = std::str::from_utf8(&output.stderr).unwrap_or("Failed to read stderr"); + panic!( + "Command failed with status: {}\nstdout: {}\nstderr: {}", + output.status, stdout, stderr + ); + } + } + + /// Function to create default config with specified parameters. + pub fn set_config( + cipher_prefs: &security::Policy, + keypair: CertKeyPair, + ) -> Result { + let mut builder = Builder::new(); + builder + .set_security_policy(cipher_prefs) + .expect("Unable to set config cipher preferences"); + builder + .set_verify_host_callback(InsecureAcceptAllCertificatesHandler {}) + .expect("Unable to set a host verify callback."); + builder + .load_pem(keypair.cert(), keypair.key()) + .expect("Unable to load cert/pem"); + builder.trust_pem(keypair.cert()).expect("load cert pem"); + builder.build() + } + /// Test to create new config, set security policy, host_callback information, load/trust certs, and build config. #[test] fn test_set_config() { @@ -87,17 +314,14 @@ mod tests { .unwrap(); } - /// Test which creates a TestPair from config using `rsa_4096_sha512`. Only measures a pair handshake. + /// Test which creates a TestPair from config using `rsa_4096_sha512`. Only measures a pair handshake. #[test] fn test_rsa_handshake() { valgrind_test("test_rsa_handshake", |ctrl| { ctrl.stop_instrumentation(); - // Example usage with RSA keypair (default) let keypair_rsa = CertKeyPair::default(); let config = set_config(&security::DEFAULT_TLS13, keypair_rsa)?; - // Create a pair (client + server) using that config, start handshake measurement let mut pair = TestPair::from_config(&config); - // Assert a successful handshake ctrl.start_instrumentation(); assert!(pair.handshake().is_ok()); ctrl.stop_instrumentation(); @@ -105,66 +329,4 @@ mod tests { }) .unwrap(); } - /// Function to run specified test using valgrind - fn run_valgrind_test(test_name: &str) { - let exe_path = std::env::args().next().unwrap(); - create_dir_all(Path::new("target/cg_artifacts")).unwrap(); - let output_file = format!("target/cg_artifacts/cachegrind_{}.out", test_name); - let output_command = format!("--cachegrind-out-file={}", &output_file); - let mut command = Command::new("valgrind"); - command - .args(["--tool=cachegrind", &output_command, &exe_path, test_name]) - // Ensures that the recursive call is made to the actual harness code block rather than back to this function - .env_remove("ENABLE_VALGRIND"); - - println!("Running command: {:?}", command); - let status = command.status().expect("Failed to execute valgrind"); - - if !status.success() { - panic!("Valgrind failed"); - } - - let annotate_output = Command::new("cg_annotate") - .arg(&output_file) - .output() - .expect("Failed to run cg_annotate"); - - if !annotate_output.status.success() { - panic!("cg_annotate failed"); - } - create_dir_all(Path::new("target/perf_outputs")).unwrap(); - let annotate_file = format!("target/perf_outputs/{}.annotated.txt", test_name); - let mut file = File::create(&annotate_file).expect("Failed to create annotation file"); - file.write_all(&annotate_output.stdout) - .expect("Failed to write annotation file"); - - let count = find_instruction_count(&annotate_file) - .expect("Failed to get instruction count from file"); - // This is temporary code to showcase the future diff functionality, here the code regresses by 10% each time so this test will almost always fail - let new_count = count + count / 10; - let diff = new_count - count; - assert!(diff <= self::MAX_DIFF, "Instruction count difference in {} exceeds the threshold, regression of {} instructions", test_name, diff); - } - - /// Parses the annotated file for the overall instruction count total - fn find_instruction_count(file_path: &str) -> Result { - let path = Path::new(file_path); - let file = File::open(path)?; - let reader = io::BufReader::new(file); - // Example of the line being parsed: - // "79,278,369 (100.0%) PROGRAM TOTALS" - for line in reader.lines() { - let line = line?; - if line.contains("PROGRAM TOTALS") { - if let Some(instructions) = line.split_whitespace().next() { - return instructions - .replace(',', "") - .parse::() - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)); - } - } - } - - panic!("Failed to find instruction count in annotated file"); - } } From f6abd5d37adef3eb218b1588d2cc77e7499db6e9 Mon Sep 17 00:00:00 2001 From: Doug Chapman <54039637+dougch@users.noreply.github.com> Date: Thu, 8 Aug 2024 10:52:21 -0700 Subject: [PATCH 13/19] fix(ci): partially revert checking out head from current clone. (#4693) --- codebuild/bin/install_s2n_head.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/codebuild/bin/install_s2n_head.sh b/codebuild/bin/install_s2n_head.sh index 3fbb3f609be..e93c9b4558a 100755 --- a/codebuild/bin/install_s2n_head.sh +++ b/codebuild/bin/install_s2n_head.sh @@ -27,7 +27,8 @@ if [ "$#" -ne "1" ]; then fi clone(){ - git clone --branch main --single-branch . "$SRC_ROOT"/s2n_head + # Path differences for internal builds mean we always need to go back to GitHub for head. + git clone --branch main --single-branch "$CLONE_SRC" "$SRC_ROOT"/s2n_head } # CMake(nix) and Make are using different directory structures. @@ -35,9 +36,13 @@ set +u if [[ "$IN_NIX_SHELL" ]]; then export DEST_DIR="$SRC_ROOT"/build/bin export EXTRA_BUILD_FLAGS="" + # Work around issue cloning inside a nix devshell https://github.com/NixOS/nixpkgs/issues/299949 + export CLONE_SRC="." else export DEST_DIR="$SRC_ROOT"/bin export EXTRA_BUILD_FLAGS="-DCMAKE_PREFIX_PATH=$LIBCRYPTO_ROOT" + # Work around different pathing issues for internal rel. + export CLONE_SRC="https://github.com/aws/s2n-tls" fi set -u From b356b868921dfb6d61a24a673bf0d6659a62f165 Mon Sep 17 00:00:00 2001 From: Jou Ho <43765840+jouho@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:21:46 -0700 Subject: [PATCH 14/19] fix: upload fuzz output to s3 when test fails (#4694) --- tests/fuzz/runFuzzTest.sh | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/fuzz/runFuzzTest.sh b/tests/fuzz/runFuzzTest.sh index a0ac257e6d9..79a275defc5 100755 --- a/tests/fuzz/runFuzzTest.sh +++ b/tests/fuzz/runFuzzTest.sh @@ -202,24 +202,6 @@ then if [ $EXPECTED_TEST_FAILURE == 1 ]; then - # Store corpus to S3 to be used for debugging if the test is negative - unset LD_PRELOAD - unset LD_LIBRARY_PATH - if [ "$CORPUS_UPLOAD_LOC" != "none" ]; then - printf "Zipping corpus files...\n" - zip -r ./corpus/${TEST_NAME}.zip ./corpus/${TEST_NAME}/ - - printf "Uploading zipped corpus file to S3 bucket...\n" - aws s3 cp ./corpus/${TEST_NAME}.zip $CORPUS_UPLOAD_LOC/${TEST_NAME}/corpus_$(date +%Y-%m-%d-%T).zip - fi - - # Store generated output files in the S3 bucket. - if [ "$ARTIFACT_UPLOAD_LOC" != "none" ]; then - printf "Uploading output files to S3 bucket...\n" - aws s3 cp ./${TEST_NAME}_output.txt ${ARTIFACT_UPLOAD_LOC}/${TEST_NAME}/output_$(date +%Y-%m-%d-%T).txt - aws s3 cp ./${TEST_NAME}_results.txt ${ARTIFACT_UPLOAD_LOC}/${TEST_NAME}/results_$(date +%Y-%m-%d-%T).txt - fi - # Clean up LibFuzzer corpus files if the test is negative. printf "\n" rm -f leak-* crash-* @@ -262,5 +244,23 @@ then else cat ${TEST_NAME}_output.txt printf "\033[31;1mFAILED\033[0m %s, %6d features covered\n" "$TEST_INFO" $FEATURE_COVERAGE + + # Store corpus to S3 to be used for debugging if the test fails + unset LD_PRELOAD + unset LD_LIBRARY_PATH + if [ "$CORPUS_UPLOAD_LOC" != "none" ]; then + printf "Zipping corpus files...\n" + zip -r ./corpus/${TEST_NAME}.zip ./corpus/${TEST_NAME}/ + + printf "Uploading zipped corpus file to S3 bucket...\n" + aws s3 cp ./corpus/${TEST_NAME}.zip $CORPUS_UPLOAD_LOC/${TEST_NAME}/corpus_$(date +%Y-%m-%d-%T).zip + fi + + # Store generated output files in the S3 bucket. + if [ "$ARTIFACT_UPLOAD_LOC" != "none" ]; then + printf "Uploading output files to S3 bucket...\n" + aws s3 cp ./${TEST_NAME}_output.txt ${ARTIFACT_UPLOAD_LOC}/${TEST_NAME}/output_$(date +%Y-%m-%d-%T).txt + aws s3 cp ./${TEST_NAME}_results.txt ${ARTIFACT_UPLOAD_LOC}/${TEST_NAME}/results_$(date +%Y-%m-%d-%T).txt + fi exit -1 fi From e8ca8911c5b2f2361687dec1467c45cd54d00b3f Mon Sep 17 00:00:00 2001 From: maddeleine <59030281+maddeleine@users.noreply.github.com> Date: Fri, 9 Aug 2024 10:08:22 -0700 Subject: [PATCH 15/19] Merge commit from fork Co-authored-by: Lindsay Stewart --- tests/unit/s2n_cert_status_extension_test.c | 5 ++ tests/unit/s2n_certificate_extensions_test.c | 7 ++ tests/unit/s2n_mutual_auth_test.c | 78 ++++++++++++++++++++ tls/s2n_connection.c | 7 +- 4 files changed, 91 insertions(+), 6 deletions(-) diff --git a/tests/unit/s2n_cert_status_extension_test.c b/tests/unit/s2n_cert_status_extension_test.c index 6bae421d76a..7d5e5cc02b1 100644 --- a/tests/unit/s2n_cert_status_extension_test.c +++ b/tests/unit/s2n_cert_status_extension_test.c @@ -108,8 +108,13 @@ int main(int argc, char **argv) /* Test recv */ { + /* Disable x509 validation so that the OCSP test data can be successfully received. */ + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + struct s2n_connection *conn = NULL; EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); struct s2n_stuffer stuffer = { 0 }; diff --git a/tests/unit/s2n_certificate_extensions_test.c b/tests/unit/s2n_certificate_extensions_test.c index 4f3681e7c40..73b83ef5017 100644 --- a/tests/unit/s2n_certificate_extensions_test.c +++ b/tests/unit/s2n_certificate_extensions_test.c @@ -77,6 +77,11 @@ static int s2n_setup_connection_for_ocsp_validate_test(struct s2n_connection **c nconn->actual_protocol_version = S2N_TLS13; nconn->handshake_params.our_chain_and_key = chain_and_key; + /* The OCSP tests ensure that an OCSP response can be successfully sent/received. They do NOT + * ensure that the OCSP response is correctly validated. + */ + nconn->x509_validator.skip_cert_validation = true; + POSIX_GUARD(s2n_connection_allow_all_response_extensions(nconn)); nconn->status_type = S2N_STATUS_REQUEST_OCSP; @@ -194,6 +199,7 @@ int main(int argc, char **argv) EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); conn->actual_protocol_version = S2N_TLS13; conn->handshake_params.our_chain_and_key = chain_and_key; + conn->x509_validator.skip_cert_validation = true; DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); @@ -210,6 +216,7 @@ int main(int argc, char **argv) EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); conn->actual_protocol_version = S2N_TLS13; conn->handshake_params.our_chain_and_key = chain_and_key; + conn->x509_validator.skip_cert_validation = true; DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); diff --git a/tests/unit/s2n_mutual_auth_test.c b/tests/unit/s2n_mutual_auth_test.c index 03b6881304d..c3d307cbd1e 100644 --- a/tests/unit/s2n_mutual_auth_test.c +++ b/tests/unit/s2n_mutual_auth_test.c @@ -306,6 +306,84 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); } + /* Ensure that the client's certificate is validated, regardless of how client auth was enabled */ + { + typedef enum { + TEST_ENABLE_WITH_CONFIG, + TEST_ENABLE_WITH_CONN_BEFORE_CONFIG, + TEST_ENABLE_WITH_CONN_AFTER_CONFIG, + TEST_COUNT, + } test_case; + + for (test_case test = TEST_ENABLE_WITH_CONFIG; test < TEST_COUNT; test++) { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + /* The client trusts the server's cert, and sends the same cert to the server. */ + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + struct host_verify_data verify_data_allow = { .allow = 1 }; + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(server_config, verify_host_fn, &verify_data_allow)); + + /* The server sends its cert, but does NOT trust the client's cert. This should always + * cause certificate validation to fail on the server. + */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + 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)); + + switch (test) { + case TEST_ENABLE_WITH_CONFIG: + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + break; + case TEST_ENABLE_WITH_CONN_BEFORE_CONFIG: + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + break; + case TEST_ENABLE_WITH_CONN_AFTER_CONFIG: + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + break; + default: + FAIL_MSG("Invalid test case"); + } + + 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_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CERT_UNTRUSTED); + + /* Ensure that a client certificate was received on the server, indicating that the + * validation error occurred when processing the client's certificate, rather than the + * server's. + */ + uint8_t *client_cert_chain = NULL; + uint32_t client_cert_chain_len = 0; + EXPECT_SUCCESS(s2n_connection_get_client_cert_chain(server_conn, + &client_cert_chain, &client_cert_chain_len)); + EXPECT_TRUE(client_cert_chain_len > 0); + } + } + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); EXPECT_SUCCESS(s2n_config_free(config)); free(cert_chain_pem); diff --git a/tls/s2n_connection.c b/tls/s2n_connection.c index 1f2d2db8348..090ec3bc476 100644 --- a/tls/s2n_connection.c +++ b/tls/s2n_connection.c @@ -303,12 +303,7 @@ int s2n_connection_set_config(struct s2n_connection *conn, struct s2n_config *co s2n_x509_validator_wipe(&conn->x509_validator); - s2n_cert_auth_type auth_type = S2N_CERT_AUTH_NONE; - POSIX_GUARD_RESULT(s2n_connection_and_config_get_client_auth_type(conn, config, &auth_type)); - - int8_t dont_need_x509_validation = (conn->mode == S2N_SERVER) && (auth_type == S2N_CERT_AUTH_NONE); - - if (config->disable_x509_validation || dont_need_x509_validation) { + if (config->disable_x509_validation) { POSIX_GUARD(s2n_x509_validator_init_no_x509_validation(&conn->x509_validator)); } else { POSIX_GUARD(s2n_x509_validator_init(&conn->x509_validator, &config->trust_store, config->check_ocsp)); From 79c0f1b434742d9f1152c48d3781433649f6f8fe Mon Sep 17 00:00:00 2001 From: maddeleine <59030281+maddeleine@users.noreply.github.com> Date: Fri, 9 Aug 2024 12:40:57 -0700 Subject: [PATCH 16/19] chore: Rust bindings bump v0.3.0 (#4697) --- bindings/rust/s2n-tls-hyper/Cargo.toml | 4 ++-- bindings/rust/s2n-tls-sys/templates/Cargo.template | 2 +- bindings/rust/s2n-tls-tokio/Cargo.toml | 4 ++-- bindings/rust/s2n-tls/Cargo.toml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bindings/rust/s2n-tls-hyper/Cargo.toml b/bindings/rust/s2n-tls-hyper/Cargo.toml index 3a1b019a939..3e5ec5da6ea 100644 --- a/bindings/rust/s2n-tls-hyper/Cargo.toml +++ b/bindings/rust/s2n-tls-hyper/Cargo.toml @@ -13,8 +13,8 @@ publish = false default = [] [dependencies] -s2n-tls = { version = "=0.2.11", path = "../s2n-tls" } -s2n-tls-tokio = { version = "=0.2.11", path = "../s2n-tls-tokio" } +s2n-tls = { version = "=0.3.0", path = "../s2n-tls" } +s2n-tls-tokio = { version = "=0.3.0", path = "../s2n-tls-tokio" } hyper = { version = "1" } hyper-util = { version = "0.1", features = ["client-legacy", "tokio", "http1"] } tower-service = { version = "0.3" } diff --git a/bindings/rust/s2n-tls-sys/templates/Cargo.template b/bindings/rust/s2n-tls-sys/templates/Cargo.template index be03be8426c..0906fa10f0a 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.11" +version = "0.3.0" 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 b593e008f5b..798f3753df5 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.11" +version = "0.3.0" 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.11", path = "../s2n-tls" } +s2n-tls = { version = "=0.3.0", 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 c5c84c5859c..500373c09aa 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.11" +version = "0.3.0" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" @@ -21,7 +21,7 @@ unstable-testing = [] [dependencies] errno = { version = "0.3" } libc = "0.2" -s2n-tls-sys = { version = "=0.2.11", path = "../s2n-tls-sys", features = ["internal"] } +s2n-tls-sys = { version = "=0.3.0", path = "../s2n-tls-sys", features = ["internal"] } pin-project-lite = "0.2" hex = "0.4" From 6578f88c394de66f21e10263715d254cbd39c714 Mon Sep 17 00:00:00 2001 From: Lindsay Stewart Date: Fri, 9 Aug 2024 22:20:30 -0700 Subject: [PATCH 17/19] docs: add pq to usage guide (#4677) --- bin/s2nc.c | 2 + bin/s2nd.c | 2 + bindings/rust/s2n-tls/src/security.rs | 5 + crypto/s2n_libcrypto.c | 2 +- crypto/s2n_libcrypto.h | 2 +- docs/BUILD.md | 4 + docs/usage-guide/topics/SUMMARY.md | 1 + docs/usage-guide/topics/ch15-post-quantum.md | 106 +++++++++++++++++++ tests/unit/s2n_security_policies_test.c | 22 ++++ tls/s2n_security_policies.c | 15 +++ 10 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 docs/usage-guide/topics/ch15-post-quantum.md diff --git a/bin/s2nc.c b/bin/s2nc.c index 5781948e2e3..ff15b66c887 100644 --- a/bin/s2nc.c +++ b/bin/s2nc.c @@ -33,6 +33,7 @@ #include "api/unstable/npn.h" #include "api/unstable/renegotiate.h" #include "common.h" +#include "crypto/s2n_libcrypto.h" #include "error/s2n_errno.h" #include "tls/s2n_connection.h" @@ -591,6 +592,7 @@ int main(int argc, char *const *argv) } GUARD_EXIT(s2n_init(), "Error running s2n_init()"); + printf("libcrypto: %s\n", s2n_libcrypto_get_version_name()); if ((r = getaddrinfo(host, port, &hints, &ai_list)) != 0) { fprintf(stderr, "error: %s\n", gai_strerror(r)); diff --git a/bin/s2nd.c b/bin/s2nd.c index 77733899e7a..08109807d79 100644 --- a/bin/s2nd.c +++ b/bin/s2nd.c @@ -34,6 +34,7 @@ #include "api/s2n.h" #include "api/unstable/npn.h" #include "common.h" +#include "crypto/s2n_libcrypto.h" #include "utils/s2n_safety.h" #define MAX_CERTIFICATES 50 @@ -565,6 +566,7 @@ int main(int argc, char *const *argv) } GUARD_EXIT(s2n_init(), "Error running s2n_init()"); + printf("libcrypto: %s\n", s2n_libcrypto_get_version_name()); printf("Listening on %s:%s\n", host, port); diff --git a/bindings/rust/s2n-tls/src/security.rs b/bindings/rust/s2n-tls/src/security.rs index e3684a67336..8b23307c1cd 100644 --- a/bindings/rust/s2n-tls/src/security.rs +++ b/bindings/rust/s2n-tls/src/security.rs @@ -87,9 +87,14 @@ pub const DEFAULT_TLS13: Policy = policy!("default_tls13"); #[cfg(feature = "pq")] pub const TESTING_PQ: Policy = policy!("PQ-TLS-1-0-2021-05-26"); +#[cfg(feature = "pq")] +pub const DEFAULT_PQ: Policy = policy!("default_pq"); + pub const ALL_POLICIES: &[Policy] = &[ DEFAULT, DEFAULT_TLS13, #[cfg(feature = "pq")] TESTING_PQ, + #[cfg(feature = "pq")] + DEFAULT_PQ, ]; diff --git a/crypto/s2n_libcrypto.c b/crypto/s2n_libcrypto.c index 9e40500da6e..166e3bf86e3 100644 --- a/crypto/s2n_libcrypto.c +++ b/crypto/s2n_libcrypto.c @@ -55,7 +55,7 @@ * symbol OpenSSL_version binded to at link-time. This can be used as * verification at run-time that s2n linked against the expected libcrypto. */ -static const char *s2n_libcrypto_get_version_name(void) +const char *s2n_libcrypto_get_version_name(void) { return SSLeay_version(SSLEAY_VERSION); } diff --git a/crypto/s2n_libcrypto.h b/crypto/s2n_libcrypto.h index 9e7aff882b8..7ec83557eaf 100644 --- a/crypto/s2n_libcrypto.h +++ b/crypto/s2n_libcrypto.h @@ -18,5 +18,5 @@ #include "utils/s2n_result.h" S2N_RESULT s2n_libcrypto_validate_runtime(void); - +const char *s2n_libcrypto_get_version_name(void); bool s2n_libcrypto_supports_flag_no_check_time(); diff --git a/docs/BUILD.md b/docs/BUILD.md index a47ccc681d8..7c2640411ae 100644 --- a/docs/BUILD.md +++ b/docs/BUILD.md @@ -68,6 +68,10 @@ cmake --install build Note that we currently do not support building on Windows. See https://github.com/aws/s2n-tls/issues/497 for more information. +Using the commands above, the libraries and headers will be located in the `s2n-tls-install` directory. + +The s2nc and s2nd test utilities are not installed by default, but can be found in the `build/bin` directory. To also install s2nc and s2nd, add `-DS2N_INSTALL_S2NC_S2ND=1` to the cmake command. + ## Consuming s2n-tls via CMake s2n-tls ships with modern CMake finder scripts if CMake is used for the build. To take advantage of this from your CMake script, all you need to do to compile and link against s2n-tls in your project is: diff --git a/docs/usage-guide/topics/SUMMARY.md b/docs/usage-guide/topics/SUMMARY.md index b365246a4ca..308864e4cec 100644 --- a/docs/usage-guide/topics/SUMMARY.md +++ b/docs/usage-guide/topics/SUMMARY.md @@ -15,3 +15,4 @@ - [Offloading Private Key Operations](./ch12-private-key-ops.md) - [Pre-shared Keys](./ch13-preshared-keys.md) - [Early Data](./ch14-early-data.md) +- [Post Quantum Support](./ch15-post-quantum.md) diff --git a/docs/usage-guide/topics/ch15-post-quantum.md b/docs/usage-guide/topics/ch15-post-quantum.md new file mode 100644 index 00000000000..c9076876a4c --- /dev/null +++ b/docs/usage-guide/topics/ch15-post-quantum.md @@ -0,0 +1,106 @@ +# Post Quantum (PQ) Support + +s2n-tls supports post-quantum key exchange for TLS1.3. Currently, only [Kyber](https://pq-crystals.org/kyber/) is supported. See the draft IETF standard: https://datatracker.ietf.org/doc/html/draft-ietf-tls-hybrid-design + +Specifically, s2n-tls supports hybrid key exchange. PQ hybrid key exchange involves performing both classic ECDH key exchange and post-quantum Kyber key exchange, then combining the two resultant secrets. This strategy combines the high assurance of the classical key exchange algorithms with the quantum-resistance of the new post-quantum key exchange algorithms. If one of the two algorithms is compromised, either because advances in quantum computing make the classic algorithms insecure or because cryptographers find a flaw in the relatively new post-quantum algorithms, the secret is still secure. Hybrid post-quantum key exchange is more secure than standard key exchange, but is slower and requires more processing and more network bandwidth. + +Careful: if an s2n-tls server is configured to support post-quantum key exchange, the server will require that any client that advertises support ultimately uses post-quantum key exchange. That will result in a retry and an extra round trip if the client does not intially provide a post-quantum key share. + +## Requirements + +### AWS-LC + +s2n-tls must be built with aws-lc to use post-quantum key exchange. See the [s2n-tls build documentation](https://github.com/aws/s2n-tls/blob/main/docs/BUILD.md#building-with-a-specific-libcrypto) for how to build with aws-lc. + +If you're unsure what cryptography library s2n-tls is built against, trying running s2nd or s2nc: +``` +> s2nd localhost 8000 +libcrypto: AWS-LC +Listening on localhost:8000 +``` + +### Security Policy + +Post-quantum key exchange is enabled by configuring a security policy (see [Security Policies](./ch06-security-policies.md)) that supports post-quantum key exchange algorithms. + +"default_pq" is the equivalent of "default_tls13", but with PQ support. Like the other default policies, "default_pq" may change as a result of library updates. The fixed, numbered equivalent of "default_pq" is currently "20240730". For previous defaults, see the "Default Policy History" section below. + +Other available PQ policies are compared in the tables below. + +### Chart: Security Policy Version To PQ Hybrid Key Exchange Methods + +| Version | secp256r1+kyber768 | x25519+kyber768 | secp384r1+kyber768 | secp521r1+kyber1024 | secp256r1+kyber512 | x25519+kyber512 | +|-----------------------|--------------------|-----------------|--------------------|---------------------|--------------------|-----------------| +| default_pq / 20240730 | X | X | X | X | X | X | +| PQ-TLS-1-2-2023-12-15 | X | | X | X | X | | +| PQ-TLS-1-2-2023-12-14 | X | | X | X | X | | +| PQ-TLS-1-2-2023-12-13 | X | | X | X | X | | +| PQ-TLS-1-2-2023-10-10 | X | X | X | X | X | X | +| PQ-TLS-1-2-2023-10-09 | X | X | X | X | X | X | +| PQ-TLS-1-2-2023-10-08 | X | X | X | X | X | X | +| PQ-TLS-1-2-2023-10-07 | X | X | X | X | X | X | +| PQ-TLS-1-3-2023-06-01 | X | X | X | X | X | X | + +### Chart: Security Policy Version To Classic Key Exchange + +If the peer doesn't support a PQ hybrid key exchange method, s2n-tls will fall back to a classical option. + +| Version | secp256r1 | x25519 | secp384r1 | secp521r1 | DHE | RSA | +|-----------------------|-----------|--------|-----------|-----------|-----|-----| +| default_pq / 20240730 | X | X | X | X | | | +| PQ-TLS-1-2-2023-12-15 | X | | X | X | X | | +| PQ-TLS-1-2-2023-12-14 | X | | X | X | | | +| PQ-TLS-1-2-2023-12-13 | X | | X | X | | X | +| PQ-TLS-1-2-2023-10-10 | X | X | X | | X | X | +| PQ-TLS-1-2-2023-10-09 | X | X | X | | X | | +| PQ-TLS-1-2-2023-10-08 | X | X | X | | X | X | +| PQ-TLS-1-2-2023-10-07 | X | X | X | | | X | +| PQ-TLS-1-3-2023-06-01 | X | | X | X | X | X | + +### Chart: Security Policy Version To Ciphers + +| Version | AES-CBC | AES-GCM | CHACHAPOLY | 3DES | +|-----------------------|---------|---------|------------|------| +| default_pq / 20240730 | X | X | X | | +| PQ-TLS-1-2-2023-12-15 | X | X | | | +| PQ-TLS-1-2-2023-12-14 | X | X | | | +| PQ-TLS-1-2-2023-12-13 | X | X | | | +| PQ-TLS-1-2-2023-10-10 | X | X | X* | X | +| PQ-TLS-1-2-2023-10-09 | X | X | X* | X | +| PQ-TLS-1-2-2023-10-08 | X | X | X* | X | +| PQ-TLS-1-2-2023-10-07 | X | X | X* | | +| PQ-TLS-1-3-2023-06-01 | X | X | X* | X | +\* only for TLS1.3 + +### Chart: Security Policy Version To Signature Schemes + +| Version | ECDSA | RSA | RSA-PSS | Legacy SHA1 | +|-----------------------|---------|-----|---------|-------------| +| default_pq / 20240730 | X | X | X | | +| PQ-TLS-1-2-2023-12-15 | X | X | X | | +| PQ-TLS-1-2-2023-12-14 | X | X | X | | +| PQ-TLS-1-2-2023-12-13 | X | X | X | | +| PQ-TLS-1-2-2023-10-10 | X | X | X | X | +| PQ-TLS-1-2-2023-10-09 | X | X | X | X | +| PQ-TLS-1-2-2023-10-08 | X | X | X | X | +| PQ-TLS-1-2-2023-10-07 | X | X | X | X | +| PQ-TLS-1-3-2023-06-01 | X | X | X | X | + +### Chart: Security Policy Version To TLS Protocol Version + +| Version | 1.2 | 1.3 | +|-----------------------|-----|-----| +| default_pq / 20240730 | X | X | +| PQ-TLS-1-2-2023-12-15 | X | X | +| PQ-TLS-1-2-2023-12-14 | X | X | +| PQ-TLS-1-2-2023-12-13 | X | X | +| PQ-TLS-1-2-2023-10-10 | X | X | +| PQ-TLS-1-2-2023-10-09 | X | X | +| PQ-TLS-1-2-2023-10-08 | X | X | +| PQ-TLS-1-2-2023-10-07 | X | X | +| PQ-TLS-1-3-2023-06-01 | X | X | + +#### Default Policy History +| Version | "default_pq" | +|------------|--------------| +| v1.5.0 | 20240730 | diff --git a/tests/unit/s2n_security_policies_test.c b/tests/unit/s2n_security_policies_test.c index 6f3e2b27ced..1672ca883e2 100644 --- a/tests/unit/s2n_security_policies_test.c +++ b/tests/unit/s2n_security_policies_test.c @@ -1090,5 +1090,27 @@ int main(int argc, char **argv) }; }; + /* Test that default_pq always matches default_tls13 */ + { + const struct s2n_security_policy *default_pq = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_pq", &default_pq)); + EXPECT_NOT_EQUAL(default_pq->kem_preferences, &kem_preferences_null); + + const struct s2n_security_policy *default_tls13 = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_tls13", &default_tls13)); + EXPECT_EQUAL(default_tls13->kem_preferences, &kem_preferences_null); + + /* If we ignore kem preferences, the two policies match */ + EXPECT_EQUAL(default_pq->minimum_protocol_version, default_tls13->minimum_protocol_version); + EXPECT_EQUAL(default_pq->cipher_preferences, default_tls13->cipher_preferences); + EXPECT_EQUAL(default_pq->signature_preferences, default_tls13->signature_preferences); + EXPECT_EQUAL(default_pq->certificate_signature_preferences, + default_tls13->certificate_signature_preferences); + EXPECT_EQUAL(default_pq->ecc_preferences, default_tls13->ecc_preferences); + EXPECT_EQUAL(default_pq->certificate_key_preferences, default_tls13->certificate_key_preferences); + EXPECT_EQUAL(default_pq->certificate_preferences_apply_locally, + default_tls13->certificate_preferences_apply_locally); + }; + END_TEST(); } diff --git a/tls/s2n_security_policies.c b/tls/s2n_security_policies.c index c36515bd3c1..d6a833ab2f8 100644 --- a/tls/s2n_security_policies.c +++ b/tls/s2n_security_policies.c @@ -59,6 +59,19 @@ const struct s2n_security_policy security_policy_20240503 = { }, }; +/* PQ default as of 07/24 */ +const struct s2n_security_policy security_policy_20240730 = { + .minimum_protocol_version = S2N_TLS12, + .cipher_preferences = &cipher_preferences_cloudfront_tls_1_2_2019, + .kem_preferences = &kem_preferences_pq_tls_1_3_2023_06, + .signature_preferences = &s2n_signature_preferences_20240501, + .certificate_signature_preferences = &s2n_certificate_signature_preferences_20201110, + .ecc_preferences = &s2n_ecc_preferences_20240501, + .rules = { + [S2N_PERFECT_FORWARD_SECRECY] = true, + }, +}; + const struct s2n_security_policy security_policy_20240603 = { .minimum_protocol_version = S2N_TLS12, .cipher_preferences = &cipher_preferences_20240603, @@ -1124,6 +1137,7 @@ struct s2n_security_policy_selection security_policy_selection[] = { { .version = "default", .security_policy = &security_policy_20240501, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, { .version = "default_tls13", .security_policy = &security_policy_20240503, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, { .version = "default_fips", .security_policy = &security_policy_20240502, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, + { .version = "default_pq", .security_policy = &security_policy_20240730, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, { .version = "20240501", .security_policy = &security_policy_20240501, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, { .version = "20240502", .security_policy = &security_policy_20240502, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, { .version = "20240503", .security_policy = &security_policy_20240503, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, @@ -1131,6 +1145,7 @@ struct s2n_security_policy_selection security_policy_selection[] = { { .version = "20240331", .security_policy = &security_policy_20240331, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, { .version = "20240417", .security_policy = &security_policy_20240417, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, { .version = "20240416", .security_policy = &security_policy_20240416, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, + { .version = "20240730", .security_policy = &security_policy_20240730, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, { .version = "ELBSecurityPolicy-TLS-1-0-2015-04", .security_policy = &security_policy_elb_2015_04, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, /* Not a mistake. TLS-1-0-2015-05 and 2016-08 are equivalent */ { .version = "ELBSecurityPolicy-TLS-1-0-2015-05", .security_policy = &security_policy_elb_2016_08, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, From 7d73fd63b432555f5f5322b3775b06ef1d2cc782 Mon Sep 17 00:00:00 2001 From: James Mayclin Date: Mon, 12 Aug 2024 16:26:47 -0700 Subject: [PATCH 18/19] chore: remove unused benchmarks (#4696) --- .gitignore | 1 - CMakeLists.txt | 20 -- Makefile | 4 - codebuild/bin/install_default_dependencies.sh | 8 - codebuild/bin/install_googlebenchmark.sh | 47 ---- codebuild/bin/s2n_codebuild.sh | 1 - docker-images/ubuntu/Dockerfile | 1 - tests/Makefile | 5 - tests/benchmark/Makefile | 51 ---- tests/benchmark/Readme.md | 91 ------- tests/benchmark/s2n_base64_benchmark.cc | 85 ------ tests/benchmark/s2n_negotiate_benchmark.cc | 29 -- tests/benchmark/s2n_pem_parse_benchmark.cc | 138 ---------- .../utils/s2n_negotiate_client_benchmark.cc | 214 --------------- .../utils/s2n_negotiate_client_benchmark.h | 18 -- .../utils/s2n_negotiate_server_benchmark.cc | 167 ------------ .../utils/s2n_negotiate_server_benchmark.h | 18 -- tests/benchmark/utils/shared_info.cc | 252 ------------------ tests/benchmark/utils/shared_info.h | 40 --- 19 files changed, 1190 deletions(-) delete mode 100755 codebuild/bin/install_googlebenchmark.sh delete mode 100644 tests/benchmark/Makefile delete mode 100644 tests/benchmark/Readme.md delete mode 100644 tests/benchmark/s2n_base64_benchmark.cc delete mode 100644 tests/benchmark/s2n_negotiate_benchmark.cc delete mode 100644 tests/benchmark/s2n_pem_parse_benchmark.cc delete mode 100644 tests/benchmark/utils/s2n_negotiate_client_benchmark.cc delete mode 100644 tests/benchmark/utils/s2n_negotiate_client_benchmark.h delete mode 100644 tests/benchmark/utils/s2n_negotiate_server_benchmark.cc delete mode 100644 tests/benchmark/utils/s2n_negotiate_server_benchmark.h delete mode 100644 tests/benchmark/utils/shared_info.cc delete mode 100644 tests/benchmark/utils/shared_info.h diff --git a/.gitignore b/.gitignore index 6eecfc650fe..f13a6eb96ac 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,6 @@ tests/unit/*_test tests/fuzz/*_test tests/fuzz/*.txt tests/fuzz/fuzz-*.log -tests/benchmark/*_benchmark bin/s2nc bin/s2nd bin/policy diff --git a/CMakeLists.txt b/CMakeLists.txt index cbce0740781..5ad6aea68d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -539,26 +539,6 @@ if (BUILD_TESTING) target_compile_options(s2nd PRIVATE -flto) endif() - if(BENCHMARK) - find_package(benchmark REQUIRED) - file(GLOB BENCHMARK_SRC "tests/benchmark/*.cc") - file(GLOB BENCHMARK_UTILS "tests/benchmark/utils/*.cc") - enable_language(CXX) - foreach(benchmark ${BENCHMARK_SRC}) - string(REGEX REPLACE ".+\\/(.+)\\.cc" "\\1" benchmark_name ${benchmark}) - add_executable(${benchmark_name} ${benchmark} "bin/echo.c" "bin/common.c" ${BENCHMARK_UTILS}) - target_include_directories(${benchmark_name} PRIVATE api) - target_include_directories(${benchmark_name} PRIVATE tests) - target_link_libraries(${benchmark_name} PUBLIC ${PROJECT_NAME} testss2n benchmark::benchmark) - - # Based off the flags in tests/benchmark/Makefile - target_compile_options(${benchmark_name} PRIVATE -pedantic -Wall -Werror -Wunused -Wcomment -Wchar-subscripts - -Wuninitialized -Wshadow -Wcast-qual -Wcast-align -Wwrite-strings -Wno-deprecated-declarations - -Wno-unknown-pragmas -Wformat-security -Wno-missing-braces -fvisibility=hidden -Wno-unreachable-code - -Wno-unused-but-set-variable) - endforeach(benchmark) - endif() - if (S2N_INTEG_TESTS) find_package (Python3 COMPONENTS Interpreter Development) file(GLOB integv2_test_files "${PROJECT_SOURCE_DIR}/tests/integrationv2/test_*.py") diff --git a/Makefile b/Makefile index e3f99c0e659..f16c403983d 100644 --- a/Makefile +++ b/Makefile @@ -88,10 +88,6 @@ fuzz-linux : export S2N_UNSAFE_FUZZING_MODE = 1 fuzz-linux : bin $(MAKE) -C tests fuzz -.PHONY : benchmark -benchmark: bin - $(MAKE) -C tests benchmark - .PHONY : coverage coverage: run-lcov run-genhtml diff --git a/codebuild/bin/install_default_dependencies.sh b/codebuild/bin/install_default_dependencies.sh index 02ed7b75a88..dbb9a814169 100755 --- a/codebuild/bin/install_default_dependencies.sh +++ b/codebuild/bin/install_default_dependencies.sh @@ -150,11 +150,3 @@ if [[ ! -x `which cmake` ]]; then ;; esac fi - -if [[ "$TESTS" == "benchmark" || "$TESTS" == "ALL" ]]; then - if [[ ! -x "$GB_INSTALL_DIR/lib/libbenchmark.a" ]]; then - mkdir -p "$GB_INSTALL_DIR"||true - codebuild/bin/install_googlebenchmark.sh "$(mktemp -d)" "$GB_INSTALL_DIR" "$OS_NAME" > /dev/null ; - fi -fi - diff --git a/codebuild/bin/install_googlebenchmark.sh b/codebuild/bin/install_googlebenchmark.sh deleted file mode 100755 index 5fc74e2ab3b..00000000000 --- a/codebuild/bin/install_googlebenchmark.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# 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. -# - -set -e - -usage() { - echo "install_googlebenchmark.sh download_dir install_dir os_name" - exit 1 -} - -if [ "$#" -ne "3" ]; then - usage -fi - -GB_DOWNLOAD_DIR=$1 -GB_INSTALL_DIR=$2 -PLATFORM=$3 - -mkdir -p "$GB_DOWNLOAD_DIR" -cd "$GB_DOWNLOAD_DIR" - -export GIT_CURL_VERBOSE=1 -echo "Downloading Google Benchmark..." -git clone https://github.com/google/benchmark.git -git clone https://github.com/google/googletest.git benchmark/googletest -cd benchmark -mkdir build && cd build -cmake ../ -DCMAKE_BUILD_TYPE=Release -make - -mkdir -p "$GB_INSTALL_DIR"/include && cp -rf "$GB_DOWNLOAD_DIR"/benchmark/include/benchmark "$GB_INSTALL_DIR"/include/ -mkdir -p "$GB_INSTALL_DIR"/lib && cp -rf "$GB_DOWNLOAD_DIR"/benchmark/build/src/*.a "$GB_INSTALL_DIR"/lib/ - - - diff --git a/codebuild/bin/s2n_codebuild.sh b/codebuild/bin/s2n_codebuild.sh index 06f8bf41d72..68e8b4c4e6e 100755 --- a/codebuild/bin/s2n_codebuild.sh +++ b/codebuild/bin/s2n_codebuild.sh @@ -128,7 +128,6 @@ if [[ "$TESTS" == "ALL" || "$TESTS" == "crt" ]]; then ./codebuild/bin/build_aws_ if [[ "$TESTS" == "ALL" || "$TESTS" == "sharedandstatic" ]]; then ./codebuild/bin/test_install_shared_and_static.sh $(mktemp -d); fi if [[ "$TESTS" == "ALL" || "$TESTS" == "dynamicload" ]]; then ./codebuild/bin/test_dynamic_load.sh $(mktemp -d); fi if [[ "$TESTS" == "ALL" || "$TESTS" == "fuzz" ]]; then (make clean && make fuzz) ; fi -if [[ "$TESTS" == "ALL" || "$TESTS" == "benchmark" ]]; then (make clean && make benchmark) ; fi if [[ "$TESTS" == "sawHMAC" ]] && [[ "$OS_NAME" == "linux" ]]; then make -C tests/saw/ tmp/verify_HMAC.log ; fi if [[ "$TESTS" == "sawDRBG" ]]; then make -C tests/saw tmp/verify_drbg.log ; fi if [[ "$TESTS" == "ALL" || "$TESTS" == "tls" ]]; then make -C tests/saw tmp/verify_handshake.log ; fi diff --git a/docker-images/ubuntu/Dockerfile b/docker-images/ubuntu/Dockerfile index 4687bbfae45..8c69104ce6a 100644 --- a/docker-images/ubuntu/Dockerfile +++ b/docker-images/ubuntu/Dockerfile @@ -64,7 +64,6 @@ RUN set -eux; \ . codebuild/bin/s2n_setup_env.sh; \ export PATH=$TEST_DEPS_DIR/clang/bin:$PATH; \ TESTS=integrationv2 codebuild/bin/s2n_install_test_dependencies.sh; \ - TESTS=benchmark codebuild/bin/s2n_install_test_dependencies.sh; \ TESTS=fuzz codebuild/bin/s2n_install_test_dependencies.sh; \ TESTS=unit BUILD_S2N=false S2N_LIBCRYPTO=openssl-1.0.2 codebuild/bin/install_default_dependencies.sh; \ TESTS=unit BUILD_S2N=false S2N_LIBCRYPTO=openssl-1.0.2-fips codebuild/bin/install_default_dependencies.sh; \ diff --git a/tests/Makefile b/tests/Makefile index cf340a9a35f..a20706063c8 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -25,10 +25,6 @@ all: libs viz ${MAKE} -C unit @echo "\033[1m ${COMPILE_INFO} \033[0;39m" -.PHONY : benchmark -benchmark: libs - ${MAKE} -C benchmark - .PHONY : valgrind valgrind: libs ${MAKE} -C unit valgrind @@ -66,5 +62,4 @@ clean: decruft ${MAKE} -C fuzz clean ${MAKE} -C viz clean ${MAKE} -C saw decruft - ${MAKE} -C benchmark decruft diff --git a/tests/benchmark/Makefile b/tests/benchmark/Makefile deleted file mode 100644 index 09a6dda9797..00000000000 --- a/tests/benchmark/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -# -# 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. -# - -SRCS=$(wildcard *.cc) -OBJS=$(SRCS:.c=.o) -TESTS=$(SRCS:.cc=) -CRYPTO_LDFLAGS = -L$(LIBCRYPTO_ROOT)/lib -BENCHMARK_LDFLAGS = -L${GB_INSTALL_DIR}/lib -lbenchmark - -# Users can specify a subset of tests to be run, otherwise run all tests. -ifeq (,$(strip ${BM_TESTS})) - BM_TESTS := ${TESTS} -endif - -.PHONY : all -.PRECIOUS : $(TESTS) - -all: $(BM_TESTS) - -include ../../s2n.mk - -CRUFT += $(wildcard *_benchmark) -LIBS += ../testlib/libtests2n.a ../../lib/libs2n.a - -# Suppress the unreachable code warning, because tests involve what should be -# unreachable code -CPPFLAGS += -Wno-unreachable-code -I../ -isystem ${GB_INSTALL_DIR}/include -Drestrict=__restrict__ -LDFLAGS += ${CRYPTO_LDFLAGS} ${LIBS} ${CRYPTO_LIBS} ${BENCHMARK_LDFLAGS} -lm -ldl -lrt -pthread - -$(BM_TESTS):: - ${CXX} ${CPPFLAGS} -o $@ $@.cc ${LDFLAGS} 2>&1 - @DYLD_LIBRARY_PATH="$(LIBCRYPTO_ROOT)/lib:$$DYLD_LIBRARY_PATH" \ - LD_LIBRARY_PATH="$(LIBCRYPTO_ROOT)/lib:$$LD_LIBRARY_PATH" \ - ./$@ - -.PHONY : clean -clean: decruft - @$(foreach test, $(TESTS), rm -f -- "${test}";) - diff --git a/tests/benchmark/Readme.md b/tests/benchmark/Readme.md deleted file mode 100644 index bb602c3af1f..00000000000 --- a/tests/benchmark/Readme.md +++ /dev/null @@ -1,91 +0,0 @@ -# s2n-tls Benchmarking -This README covers the basics on how to build the s2n-tls library to be able to use Google Benchmark in running the s2n-tls benchmarks. -##Install Google Benchmark -Follow instructions on the Google Benchmark repository to build and install [Google Benchmark](https://github.com/google/benchmark) - -## Building the s2n-tls library -#### In order to enable the s2n library to build the benchmarks the following parameters must be set: -1. `-DBUILD_TESTING=1` -2. `-DBENCHMARK=1` -3. `-DCMAKE_PREFIX_PATH="File/path/to/Google/Benchmark/"` - -#### Example: - -``` -# Starting from the top level "s2n-tls" directory, remove previous CMake build files, if any -rm -rf build - -# Initialize CMake build directory with Nina build system -cmake . -Bbuild -GNinja -DCMAKE_EXE_LINKER_FLAGS="-lcrypto -lz" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=1 -DBENCHMARK=1 -DCMAKE_PREFIX_PATH="~/benchmark/install" - -# Actually build the executable binaries -cd build -ninja - -# Run a benchmark -./bin/s2n_negotiate_benchmark -r 1 -i 5 -p ../tests/pems/ -o negotiate_data -t console localhost 8000 -``` - -**If you would like to build with a different libcrypto, include the file path in -DCMAKE_PREFIX_PATH**: - -`-DCMAKE_PREFIX_PATH="~/aws-lc/install;~/benchmark/install"` - -## Running benchmarks -Once the s2n-tls library has completed building, the benchmarks can be located in the `build/bin` folder. -The two benchmarks that are currently available are `s2n_negotiate_benchmark` and `s2n_send_recv_benchmark` - -### Benchmark Options: -Each benchmark has the ability to accept different options: - -usage: - - *s2n_benchmark* [options] host port - - host: hostname or IP address to connect to - port: port to connect to -###### Options: - -i [# of iterations] - sets the number of iterations to run each repetition - - -r [# of repetitions] - sets the number of repetitions to run each benchmark - - -w [# of warmup iterations] - sets the number of warmup runs for each benchmark - - -o [output file name] - sets the name of the output file - - -t [json|csv|console] - sets the output format of the output file - - -p [file path to pem directory] - if using secure mode, must set pem directory - - -g [google benchmark options] - sets the google benchmark options - - -d [#;#;#] - sets the size of the data that should be sent in send/recv benchmarks - - -s - run benchmarks in insecure mode - - -c - sets use_corked_io to true - - -D - print debug output to terminal - - -### s2n_negotiate_benchmark -Example: - -`./bin/s2n_negotiate_benchmark -r 1 -i 5 -p ../tests/pems/ -o negotiate_data -t console localhost 8000` - -or - -`./bin/s2n_negotiate_benchmark -r 5 -i 5 -w 10 -p ../tests/pems/ -o negotiate_data -t console localhost 8000` - - - diff --git a/tests/benchmark/s2n_base64_benchmark.cc b/tests/benchmark/s2n_base64_benchmark.cc deleted file mode 100644 index 234e3e0b642..00000000000 --- a/tests/benchmark/s2n_base64_benchmark.cc +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 -#include - -#include - -#include "api/s2n.h" - -extern "C" { -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_random.h" -} - - -class TestFixture : public benchmark::Fixture { -public: - void SetUp(const ::benchmark::State& state) { - s2n_result result; - int rc; - - memset(&r, 0, sizeof(r)); - memset(&entropy, 0, sizeof(entropy)); - - pad.resize(state.range(0)); - rc = s2n_blob_init(&r, pad.data(), pad.size()); - assert(rc == 0); - - result = s2n_get_public_random_data(&r); - assert(s2n_result_is_ok(result)); - rc = s2n_stuffer_alloc(&entropy, pad.size()); - assert(rc == 0); - rc = s2n_stuffer_write_bytes(&entropy, pad.data(), pad.size()); - assert(rc == 0); - } - - void TearDown(const ::benchmark::State& state) { - } - - std::vectorpad; - struct s2n_blob r; - struct s2n_stuffer entropy; - -}; - -BENCHMARK_DEFINE_F(TestFixture, Base64EncodeDecode)(benchmark::State& state) { - for (auto _ : state) { - struct s2n_stuffer stuffer = {0}; - struct s2n_stuffer mirror = {0}; - s2n_stuffer_write_base64(&stuffer, &entropy); - s2n_stuffer_read_base64(&stuffer, &mirror); - } -} - -BENCHMARK_REGISTER_F(TestFixture, Base64EncodeDecode)->DenseRange(1024, 1024 * 1024, 128 * 1024); - -int main(int argc, char** argv) { - ::benchmark::Initialize(&argc, argv); - - int rc = s2n_init(); - assert(rc == 0); - - if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1; - ::benchmark::RunSpecifiedBenchmarks(); - - rc = s2n_cleanup(); - assert(rc == 0); -} - - diff --git a/tests/benchmark/s2n_negotiate_benchmark.cc b/tests/benchmark/s2n_negotiate_benchmark.cc deleted file mode 100644 index 6bc3e2497c7..00000000000 --- a/tests/benchmark/s2n_negotiate_benchmark.cc +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 "utils/s2n_negotiate_client_benchmark.h" -#include "utils/s2n_negotiate_server_benchmark.h" - -int main(int argc, char** argv) { - int pid_server = fork(); - if (pid_server == 0) { - start_negotiate_benchmark_server(argc, argv); - } - else { - start_negotiate_benchmark_client(argc, argv); - } - return 0; -} diff --git a/tests/benchmark/s2n_pem_parse_benchmark.cc b/tests/benchmark/s2n_pem_parse_benchmark.cc deleted file mode 100644 index 9fd5b3716c9..00000000000 --- a/tests/benchmark/s2n_pem_parse_benchmark.cc +++ /dev/null @@ -1,138 +0,0 @@ -/* - * 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 -#include - -#include - -#include "api/s2n.h" - -extern "C" { -#include "utils/s2n_safety.h" -#include "testlib/s2n_testlib.h" -} - - -static const char *valid_pem_pairs[][2] = { - { S2N_RSA_2048_PKCS8_CERT_CHAIN, S2N_RSA_2048_PKCS8_KEY }, - { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, - { S2N_RSA_2048_PKCS1_LEAF_CERT, S2N_RSA_2048_PKCS1_KEY }, - { S2N_RSA_CERT_CHAIN_CRLF, S2N_RSA_KEY_CRLF }, - /* PEMs with no-op data before/after entries are still valid */ - { S2N_LEAF_WHITESPACE_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, - { S2N_INTERMEDIATE_WHITESPACE_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, - { S2N_ROOT_WHITESPACE_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, - { S2N_TRAILING_WHITESPACE_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, - { S2N_LEADING_COMMENT_TEXT_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, - { S2N_LONG_BASE64_LINES_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, - { S2N_MISSING_LINE_ENDINGS_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, - - /* Technically Invalid according to RFC, but that we are lenient towards */ - { S2N_INVALID_HEADER_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, - { S2N_INVALID_TRAILER_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, - { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_INVALID_TRAILER_KEY }, - { S2N_WEIRD_DASHES_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, -}; - -static const char *invalid_pem_pairs[][2] = { - /* Invalid cert PEMs and valid key PEMs */ - { S2N_UNKNOWN_KEYWORD_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, - /* Valid cert PEMs and invalid key PEMs */ - { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_INVALID_HEADER_KEY }, - { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_UNKNOWN_KEYWORD_KEY }, - /* For good measure an invalid cert and invalid key */ - { S2N_UNKNOWN_KEYWORD_CERT_CHAIN, S2N_UNKNOWN_KEYWORD_KEY }, - { S2N_NO_DASHES_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, -}; - -class PemTestFixture { -public: - void alloc() { - cert_chain_pem = new char[S2N_MAX_TEST_PEM_SIZE]; - private_key_pem = new char[S2N_MAX_TEST_PEM_SIZE]; - - chain_and_key = s2n_cert_chain_and_key_new(); - } - - void free() { - s2n_cert_chain_and_key_free(chain_and_key); - delete [] cert_chain_pem; - delete [] private_key_pem; - } - - char *cert_chain_pem; - char *private_key_pem; - struct s2n_cert_chain_and_key *chain_and_key; - - void load_pem_file(const char *cert, const char *key) { - s2n_read_test_pem(cert, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE); - s2n_read_test_pem(key, private_key_pem, S2N_MAX_TEST_PEM_SIZE); - } - - -}; - -class ValidPemTestFixture - : public benchmark::Fixture - , public PemTestFixture { -public: - - void SetUp(const ::benchmark::State& state) { - PemTestFixture::alloc(); - - load_pem_file(valid_pem_pairs[state.range(0)][0], valid_pem_pairs[state.range(0)][1]); - } - - void TearDown(const ::benchmark::State& state) { - PemTestFixture::free(); - } -}; - -class InvalidPemTestFixture - : public benchmark::Fixture - , public PemTestFixture { -public: - - void SetUp(const ::benchmark::State& state) { - PemTestFixture::alloc(); - - load_pem_file(invalid_pem_pairs[state.range(0)][0], invalid_pem_pairs[state.range(0)][1]); - } - - void TearDown(const ::benchmark::State& state) { - PemTestFixture::free(); - } -}; - -BENCHMARK_DEFINE_F(ValidPemTestFixture, Load_Valid_Pem)(benchmark::State& state) { - for (auto _ : state) { - s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem); - } -} - -BENCHMARK_DEFINE_F(InvalidPemTestFixture, Load_Invalid_Pem)(benchmark::State& state) { - for (auto _ : state) { - s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem); - } -} - -BENCHMARK_REGISTER_F(ValidPemTestFixture, Load_Valid_Pem)->DenseRange(0, s2n_array_len(valid_pem_pairs), 1); -BENCHMARK_REGISTER_F(InvalidPemTestFixture, Load_Invalid_Pem)->DenseRange(0, s2n_array_len(invalid_pem_pairs), 1); - -BENCHMARK_MAIN(); - diff --git a/tests/benchmark/utils/s2n_negotiate_client_benchmark.cc b/tests/benchmark/utils/s2n_negotiate_client_benchmark.cc deleted file mode 100644 index 6755764c5c1..00000000000 --- a/tests/benchmark/utils/s2n_negotiate_client_benchmark.cc +++ /dev/null @@ -1,214 +0,0 @@ -/* - * 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 "tests/benchmark/utils/s2n_negotiate_client_benchmark.h" -#include "tests/benchmark/utils/shared_info.h" -#include "utils/s2n_safety.h" -#include "s2n_test.h" -#include -#include -#include -#include -#include - -extern "C" { -#include "bin/common.h" -#include "tls/s2n_connection.h" -} - -static int setup_socket(int& sockfd) { - struct addrinfo hints = {}; - struct addrinfo *ai = nullptr; - struct addrinfo *ai_list = nullptr; - - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - GUARD_EXIT(getaddrinfo(host, port, &hints, &ai_list), "getaddrinfo error\n"); - - bool connected = false; - while (!connected) { - for (ai = ai_list; ai != NULL; ai = ai->ai_next) { - if ((sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) { continue; } - if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) == -1) { - close(sockfd); - continue; - } - connected = true; - if (DEBUG_PRINT) { - printf("Connected to s2nd\n"); - } - break; - } - } - - freeaddrinfo(ai_list); - return 0; -} - -static void client_handshake(benchmark::State& state, bool warmup, struct s2n_connection *conn, int sockfd) { - GUARD_EXIT(s2n_set_server_name(conn, host), "Error setting server name"); - GUARD_EXIT(s2n_connection_set_fd(conn, sockfd), "Error setting file descriptor"); - - if (benchmark_negotiate(conn, sockfd, state, warmup) != S2N_SUCCESS) { - state.SkipWithError("Negotiate Failed\n"); - } - - if (DEBUG_PRINT) { - printf("Connected to %s:%s\n", host, port); - } - - s2n_blocked_status blocked; - int shutdown_rc = s2n_shutdown(conn, &blocked); - while(shutdown_rc != 0) { - shutdown_rc = s2n_shutdown(conn, &blocked); - } - - GUARD_EXIT(s2n_connection_wipe(conn), "Error wiping connection"); -} - -static void benchmark_single_suite_client(benchmark::State& state) { - int sockfd = state.range(2); - config = s2n_config_new(); - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - size_t warmup_iters = state.range(1); - - GUARD_EXIT_NULL(config); - - /* In order to specify each cipher suite from the cipher_preferences_test_all_tls12 - * list, a custom cipher_suite list must be created with only one cipher suite. In - * order to use this new list, a cipher preference and security policy must also be - * created */ - struct s2n_cipher_suite *cipher_suites_benchmark[] = { - all_suites[state.range(0)] - }; - - const struct s2n_cipher_preferences cipher_preferences_benchmark = { - 1, /* count */ - cipher_suites_benchmark, /* suites */ - }; - - const struct s2n_security_policy security_policy_benchmark = { - S2N_SSLv3, /* minimum_protocol_version */ - &cipher_preferences_benchmark, /* cipher_preferences */ - &kem_preferences_kms_pq_tls_1_0_2020_07, /* kem_preferences */ - &s2n_signature_preferences_20201021, /* signature_preferences */ - NULL, /* certificate_signature_preferences */ - &s2n_ecc_preferences_20201021, /* ecc_preferences */ - }; - - config->security_policy = &security_policy_benchmark; - - struct verify_data *unsafe_verify_data = (verify_data *) malloc(sizeof(verify_data)); - s2n_status_request_type type = S2N_STATUS_REQUEST_NONE; - - GUARD_EXIT(s2n_config_set_status_request_type(config, type), - "OCSP validation is not supported by the linked libCrypto implementation. It cannot be set."); - - GUARD_EXIT(s2n_config_set_verify_host_callback(config, unsafe_verify_host, unsafe_verify_data), - "Error setting host name verification function\n"); - - unsafe_verify_data->trusted_host = host; - - if (!conn_settings.insecure) { - const char *ca_dir = NULL; - if(all_suites[state.range(0)]->auth_method == S2N_AUTHENTICATION_RSA) { - std::string ca_file = "rsa_2048_sha384_client_cert.pem"; - std::string pem_file_location = pem_dir + ca_file; - GUARD_EXIT(s2n_config_set_verification_ca_location(config, pem_file_location.c_str(), ca_dir), - "Error setting CA file for RSA trust store\n"); - } - else { - std::string ca_file = "ecdsa_p256_pkcs1_cert.pem"; - std::string pem_file_location = pem_dir + ca_file; - GUARD_EXIT(s2n_config_set_verification_ca_location(config, pem_file_location.c_str(), ca_dir), - "Error setting CA file for ECDSA trust store\n"); - } - } - else { - GUARD_EXIT(s2n_config_disable_x509_verification(config), "Error disabling X.509 validation"); - } - - if (conn == NULL) { - print_s2n_error("Error getting new connection"); - exit(1); - } - - GUARD_EXIT(s2n_connection_set_config(conn, config), "Error setting configuration"); - - GUARD_EXIT(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_OPTIONAL), - "Error setting ClientAuth optional"); - - if (conn_settings.use_corked_io) { - GUARD_EXIT(s2n_connection_use_corked_io(conn), "Client: Error setting corked io"); - } - - for (size_t i = 0; i < warmup_iters; i++) { - client_handshake(state, true, conn, sockfd); - } - for (auto _ : state) { - state.PauseTiming(); - client_handshake(state, false, conn, sockfd); - } - free(unsafe_verify_data); - GUARD_EXIT(s2n_config_free(config), "Error freeing configuration"); - GUARD_EXIT(s2n_connection_free(conn), "Error freeing connection"); -} - -int start_negotiate_benchmark_client(int argc, char** argv) { - int use_corked_io = 0; - int insecure = 0; - int sockfd = 0; - conn_settings = {0}; - char bench_format[100] = "--benchmark_out_format="; - char bench_out[100] = "--benchmark_out=client_"; - std::string file_prefix; - std::string gb_options; - std::vector data_sizes; - long int warmup_iters = 1; - size_t iterations = 1; - size_t repetitions = 1; - - argument_parse(argc, argv, use_corked_io, insecure, bench_format, file_prefix, warmup_iters, iterations, repetitions, - gb_options, data_sizes); - - strcat(bench_out, file_prefix.c_str()); - std::vector argv_bench(argv, argv + argc); - argv_bench.push_back(bench_out); - argv_bench.push_back(bench_format); - argv_bench.push_back(nullptr); - argv = argv_bench.data(); - argc = argv_bench.size(); - - conn_settings.use_corked_io = use_corked_io; - conn_settings.insecure = insecure; - - s2n_init(); - - GUARD_EXIT(setup_socket(sockfd), "Client socket setup failed\n"); - - for (long int current_suite = 0; current_suite < num_suites; current_suite++) { - std::string bench_name = std::string("Client: ") + all_suites[current_suite]->name; - - benchmark::RegisterBenchmark(bench_name.c_str(), benchmark_single_suite_client)->Repetitions(repetitions) - ->ReportAggregatesOnly()->Iterations(iterations)->Args({current_suite, warmup_iters, sockfd}); - } - ::benchmark::Initialize(&argc, argv); - ::benchmark::RunSpecifiedBenchmarks(); - - s2n_cleanup(); - close(sockfd); - return 0; -} diff --git a/tests/benchmark/utils/s2n_negotiate_client_benchmark.h b/tests/benchmark/utils/s2n_negotiate_client_benchmark.h deleted file mode 100644 index 905649a9019..00000000000 --- a/tests/benchmark/utils/s2n_negotiate_client_benchmark.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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. - */ - -#pragma once - -int start_negotiate_benchmark_client(int argc, char **argv); diff --git a/tests/benchmark/utils/s2n_negotiate_server_benchmark.cc b/tests/benchmark/utils/s2n_negotiate_server_benchmark.cc deleted file mode 100644 index 0095e4c0837..00000000000 --- a/tests/benchmark/utils/s2n_negotiate_server_benchmark.cc +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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 "tests/benchmark/utils/s2n_negotiate_server_benchmark.h" -#include "tests/benchmark/utils/shared_info.h" -#include -#include -#include -#include - -extern "C" { -#include "bin/common.h" -#include "tls/s2n_connection.h" -} - -static int server_handshake(benchmark::State& state, bool warmup, struct s2n_connection *conn, int connectionfd) { - if (!conn) { - print_s2n_error("Error getting new s2n connection"); - exit(1); - } - - s2n_setup_server_connection(conn, connectionfd, config, conn_settings); - - GUARD_EXIT(benchmark_negotiate(conn, connectionfd, state, warmup), "Server negotiation failed\n"); - if (conn_settings.mutual_auth) { - if (!s2n_connection_client_cert_used(conn)) { - print_s2n_error("Error: Mutual Auth was required, but not negotiated"); - exit(1); - } - } - - s2n_blocked_status blocked; - int shutdown_rc = s2n_shutdown(conn, &blocked); - while(shutdown_rc != 0) { - shutdown_rc = s2n_shutdown(conn, &blocked); - } - - GUARD_RETURN(s2n_connection_wipe(conn), "Error wiping connection"); - - return 0; -} - -static int benchmark_single_suite_server(benchmark::State& state) { - int connectionfd = state.range(2); - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - size_t warmup_iters = state.range(1); - - for (size_t i = 0; i < warmup_iters; i++) { - server_handshake(state, true, conn, connectionfd); - } - for (auto _ : state) { - state.PauseTiming(); - server_handshake(state, false, conn, connectionfd); - } - - GUARD_RETURN(s2n_connection_free(conn), "Error freeing connection"); - - return 0; -} - -int start_negotiate_benchmark_server(int argc, char **argv) { - const char *cipher_prefs = "test_all_tls12"; - struct addrinfo hints = {}; - struct addrinfo *ai; - conn_settings = {0}; - int use_corked_io, insecure, connectionfd, sockfd = 0; - char bench_format[100] = "--benchmark_out_format="; - std::string file_prefix; - std::string gb_options; - std::vector data_sizes; - long int warmup_iters = 1; - size_t iterations = 1; - size_t repetitions = 1; - - argument_parse(argc, argv, use_corked_io, insecure, bench_format, file_prefix, warmup_iters, iterations, repetitions, - gb_options, data_sizes); - - std::string log_output_name = "server_" + file_prefix; - FILE* write_log = freopen(log_output_name.c_str(), "w", stdout); - - std::vector argv_bench(argv, argv + argc); - argv_bench.push_back(bench_format); - argv_bench.push_back(nullptr); - argc = argv_bench.size(); - - const char *session_ticket_key_file_path = NULL; - - int setsockopt_value = 1; - int max_early_data = 0; - conn_settings.session_ticket = 1; - conn_settings.session_cache = 0; - conn_settings.max_conns = -1; - conn_settings.psk_list_len = 0; - conn_settings.insecure = insecure; - conn_settings.use_corked_io = use_corked_io; - - s2n_init(); - - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { - fprintf(stderr, "Error disabling SIGPIPE\n"); - exit(1); - } - - GUARD_EXIT(getaddrinfo(host, port, &hints, &ai), "getaddrinfo error\n"); - GUARD_EXIT((sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)), "socket error\n"); - GUARD_EXIT(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &setsockopt_value, sizeof(int)), "setsockopt error\n"); - bind(sockfd, ai->ai_addr, ai->ai_addrlen); - GUARD_EXIT(listen(sockfd, 1), "listen error\n"); - - if (DEBUG_PRINT) { - printf("Listening on %s:%s\n", host, port); - } - - config = s2n_config_new(); - if (!config) { - print_s2n_error("Error getting new s2n config"); - exit(1); - } - - s2n_set_common_server_config(max_early_data, config, conn_settings, cipher_prefs, session_ticket_key_file_path); - - struct s2n_cert_chain_and_key *chain_and_key_rsa = s2n_cert_chain_and_key_new(); - GUARD_EXIT(s2n_cert_chain_and_key_load_pem(chain_and_key_rsa, rsa_certificate_chain, rsa_private_key), - "Error loading RSA certificate/key"); - - GUARD_EXIT(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key_rsa), - "Error adding RSA chain and key"); - - struct s2n_cert_chain_and_key *chain_and_key_ecdsa = s2n_cert_chain_and_key_new(); - GUARD_EXIT(s2n_cert_chain_and_key_load_pem(chain_and_key_ecdsa, ecdsa_certificate_chain, ecdsa_private_key), - "Error loading ECDSA certificate/key"); - - GUARD_EXIT(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key_ecdsa), - "Error adding ECDSA chain and key"); - - connectionfd = accept(sockfd, ai->ai_addr, &ai->ai_addrlen); - for (long int suite_num = 0; suite_num < num_suites; ++suite_num) { - std::string bench_name = std::string("Server: ") + all_suites[suite_num]->name; - - benchmark::RegisterBenchmark(bench_name.c_str(), benchmark_single_suite_server)->Repetitions(repetitions) - ->Iterations(iterations)->Args({suite_num, warmup_iters, connectionfd}); - } - - ::benchmark::Initialize(&argc, argv_bench.data()); - ::benchmark::RunSpecifiedBenchmarks(); - - close(connectionfd); - close(sockfd); - fclose(write_log); - s2n_cleanup(); - return 0; -} diff --git a/tests/benchmark/utils/s2n_negotiate_server_benchmark.h b/tests/benchmark/utils/s2n_negotiate_server_benchmark.h deleted file mode 100644 index ca90a2ff330..00000000000 --- a/tests/benchmark/utils/s2n_negotiate_server_benchmark.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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. - */ - -#pragma once - -int start_negotiate_benchmark_server(int argc, char **argv); diff --git a/tests/benchmark/utils/shared_info.cc b/tests/benchmark/utils/shared_info.cc deleted file mode 100644 index 39a5b9fe052..00000000000 --- a/tests/benchmark/utils/shared_info.cc +++ /dev/null @@ -1,252 +0,0 @@ -/* - * 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 "shared_info.h" - -extern "C" { -#include "bin/common.h" -} - -struct s2n_cipher_suite **all_suites = cipher_preferences_test_all_tls12.suites; -unsigned int num_suites = cipher_preferences_test_all_tls12.count; -const char *host = "localhost"; -const char *port = "8000"; -char const *pem_dir = ""; -int DEBUG_PRINT = 0; -struct s2n_config *config; -struct conn_settings conn_settings; - -//rsa_2048_sha384_client_cert.pem - Expires: Jul 8th, 2117 -const char *rsa_certificate_chain = - "-----BEGIN CERTIFICATE-----" - "MIIDfTCCAmWgAwIBAgIJAPUg4P1R3ctAMA0GCSqGSIb3DQEBDAUAMF8xCzAJBgNV" - "BAYTAlVTMQswCQYDVQQIDAJXQTEQMA4GA1UEBwwHU2VhdHRsZTEPMA0GA1UECgwG" - "QW1hem9uMQwwCgYDVQQLDANzMm4xEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0xNzA4" - "MDEyMjQzMzJaGA8yMTE3MDcwODIyNDMzMlowXzELMAkGA1UEBhMCVVMxCzAJBgNV" - "BAgMAldBMRAwDgYDVQQHDAdTZWF0dGxlMQ8wDQYDVQQKDAZBbWF6b24xDDAKBgNV" - "BAsMA3MybjESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOC" - "AQ8AMIIBCgKCAQEA+gyp3fZTLrvWsZ8QxBIoLItRcSveQc4HavylagfNIawpnRbu" - "39Lu2dzJSDk9so04iPm5+OLVAY8lhH9vPOSyhCL/GxikxkKgCvLWv4c4fVBeWt5Z" - "S5xgOVsaKUR9oVfhTHLlc6M/gA5NW0Wbh34DZHAFCF9noGGoKKXpnDTyApGBYTHW" - "XXEcL2sPKF/jU9maGPQzX7joHlHWSx1kiWTne1Gn7NJrmxrYUrn9VjM15bwi94gZ" - "LE5TsCCoPFIzbzyKattYFJ8xNHERcf/Ss+0VCYCAT2g0GmykjK3znpKPCRSakPfx" - "Xqfpx0MJLsh9zG1eqkQV+zc4NXursZ61ydBlLQIDAQABozowODALBgNVHQ8EBAMC" - "BDAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwFAYDVR0RBA0wC4IJMTI3LjAuMC4xMA0G" - "CSqGSIb3DQEBDAUAA4IBAQBVEGuaKg2pdynqZHiMr67WlbHqAE+9NvT5ouwb81VZ" - "/UH0hawwRJAQgSa09sUNyp1tGmV7OrLIzVyp0HcpNt4RbASmk9eDwPO/woggGWXD" - "f0go1LlKPcB/IoMvOmEX8CqqNWncYproXEaHT31hwQRtrT+bfU8MbH76eQ5vC2Fo" - "RfUfHW6mjc8plw786BdKIg0B5r9rHMTrmFeP/HlDP5fkS4wH5y9hLGqkchBDUtAa" - "wvXRARKV/xCvZMqcwPY6sqbpZ5surR4GEe9KrgodmtWYCq12TIfmucfkAicFcCQa" - "fxZRly5wMFwzjqcyM01lzDCqzhxC7nVljCJie8brb2mG" - "-----END CERTIFICATE-----"; - -//rsa_2048_sha384_client_key.pem -const char *rsa_private_key = - "-----BEGIN PRIVATE KEY-----" - "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD6DKnd9lMuu9ax" - "nxDEEigsi1FxK95Bzgdq/KVqB80hrCmdFu7f0u7Z3MlIOT2yjTiI+bn44tUBjyWE" - "f2885LKEIv8bGKTGQqAK8ta/hzh9UF5a3llLnGA5WxopRH2hV+FMcuVzoz+ADk1b" - "RZuHfgNkcAUIX2egYagopemcNPICkYFhMdZdcRwvaw8oX+NT2ZoY9DNfuOgeUdZL" - "HWSJZOd7Uafs0mubGthSuf1WMzXlvCL3iBksTlOwIKg8UjNvPIpq21gUnzE0cRFx" - "/9Kz7RUJgIBPaDQabKSMrfOeko8JFJqQ9/Fep+nHQwkuyH3MbV6qRBX7Nzg1e6ux" - "nrXJ0GUtAgMBAAECggEBAOcHnEtAtEqRsyQZ29vNCuFdN7pg1dHnEmN/WzZETvu1" - "nh1OexbCRX11yWO5v4+he4LTeUjEDBqMsBVjyNtyUp5T13CprFSiakyzYkdEIKVo" - "BEXg+pApw5461kkaxxizoa6I2gel5Z3jmQWjorflbiz2cy/xNkWw9TXZVabGJHTJ" - "OD+C6afzcImpCfdNVPQ2NvZpyNGwxDwimrxbIpowormedFjG0YscdruSAY4R1D/f" - "Z8iqoUGb22Mlu0vtGT8rb5Gz/VcnzAVjKRmExo4WBCCi7zg6vJ6QwYd9SYgObYJr" - "JztT3HtQWEk28ip4bNmLgVjDjtAAePEp0e6FwiB8nAECgYEA/jgBHmtx2YGuH6ns" - "PQEGnOyp2CGCi1lSAcH76pViOBDRnjr1oP1mOFjYqk9OgngF9M3sEnID5dl4FoDr" - "C12nyV/M9ZRlhy3Vq+5armWIUITFwKNpO1NGXQB9LPYbVA6Mt2Vgntqqc2id9/vA" - "sgZhdw4Rcjelspx+Gi3+hdlIX6ECgYEA+80uPpzyVpy4BBcYG+IDi07agn7w99LN" - "vo4/NeFlBPFYGjywaC90Ju0o8CFt8QL0E4tPv8jhWzTqh3FupKbI4R5jLlVzyoHK" - "cxdJr7WIK4vnXFtJu2tECudHVMGYhY2NkAILXfNyksYThB7/LJkhEK+f9Got8gZK" - "xXasU6EfSg0CgYBcbNojSCcNUDOROYM1LrFLzlN1y8EdjqzdDLzdLdCW166OW5tA" - "G8DVTaAAU3MUxjRMK63fiupV37nkXJyX9kXxVc47nudGvWhI6RC5BRsJQyxufDrf" - "IcicOXhJJ3UKG3wXlVkKiC+eY/PC3BnT37QBx/CZ2Rd6F6FVPVGjMjs44QKBgBSO" - "LWZDHa1gYc1DrV4pVyy6JTBd+IHinZUeu55EZiC/KvgJWEVJCmxbE+p2cCkqmo41" - "4y6+0VbGvRaNdgDO9Lsb5fDUXP19Fu/KSOOlKBaV9y8c7Kn2GbniI3qRy0erxJCq" - "+g6TXxkIPnOcrCwR3BcmnyIuwM1vIg94npy9HHbJAoGBALQa7hybvU+K9QhKJ/UA" - "Ek0GF+8uYfJtQ27U+lXVXIq85lveXAUZ7S/d5k5WklU6iZTYhljEWnLSpsXReyYi" - "4UlMKX+zTAsL3a5o7GtM0LbV8ZyecTt9k8xK8T/PdS0/w78KwTO/13OdItuMi0vx" - "Q00ZFPpn7NMd+V9tAUhofrET" - "-----END PRIVATE KEY-----"; - -//ecdsa_p256_pkcs1_cert.pem - Expires: Nov 10th, 2120 -const char *ecdsa_certificate_chain = - "-----BEGIN CERTIFICATE-----" - "MIICLDCCAdGgAwIBAgIUPYYEnK24qDzz59IIDcNLc4P2H5swCgYIKoZIzj0EAwIw" - "XzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0dGxlMQ8w" - "DQYDVQQKDAZBbWF6b24xDDAKBgNVBAsMA3MybjESMBAGA1UEAwwJbG9jYWxob3N0" - "MCAXDTIwMTIwNDA3NDg1NloYDzIxMjAxMTEwMDc0ODU2WjBfMQswCQYDVQQGEwJV" - "UzELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUxDzANBgNVBAoMBkFtYXpv" - "bjEMMAoGA1UECwwDczJuMRIwEAYDVQQDDAlsb2NhbGhvc3QwWTATBgcqhkjOPQIB" - "BggqhkjOPQMBBwNCAARhONnk1k68YnnabiHzf3AvlWwN93SOvdq6v1Grl3YEiGM1" - "W8WFH7O4cxb+otlVlhhbPzaox4EVthLExJZumx8go2kwZzAdBgNVHQ4EFgQU0ip8" - "rN6YlbtCUIueCOqfh3/J3KMwHwYDVR0jBBgwFoAU0ip8rN6YlbtCUIueCOqfh3/J" - "3KMwDwYDVR0TAQH/BAUwAwEB/zAUBgNVHREEDTALggkxMjcuMC4wLjEwCgYIKoZI" - "zj0EAwIDSQAwRgIhAJwlrxN5SDi2dC17ZPgajqZ8BZyOsNFE+gsobhMBUGN0AiEA" - "6KFJgyPGBNdQqaczkNyBcutPGqEubuah5Me6faN4qqU=" - "-----END CERTIFICATE-----"; - -//ecdsa_p384_pkcs1_key.pem -const char *ecdsa_private_key = - "-----BEGIN EC PARAMETERS-----" - "BggqhkjOPQMBBw==" - "-----END EC PARAMETERS-----" - "-----BEGIN EC PRIVATE KEY-----" - "MHcCAQEEIK4AEDQja7MDATqWWu4T0+iMFdSZH4y4+nuVzDX5ao8KoAoGCCqGSM49" - "AwEHoUQDQgAEYTjZ5NZOvGJ52m4h839wL5VsDfd0jr3aur9Rq5d2BIhjNVvFhR+z" - "uHMW/qLZVZYYWz82qMeBFbYSxMSWbpsfIA==" - "-----END EC PRIVATE KEY-----"; - -void usage() { - fprintf(stderr, "usage: s2n_benchmark [options] host port\n"); - fprintf(stderr, " host: hostname or IP address to connect to\n"); - fprintf(stderr, " port: port to connect to\n"); - fprintf(stderr, "\n Options:\n"); - fprintf(stderr, " -c\n"); - fprintf(stderr, " sets use_corked_io to true\n"); - fprintf(stderr, " -i [# of iterations]\n"); - fprintf(stderr, " sets the number of iterations to run each repetition\n"); - fprintf(stderr, " -r [# of repetitions]\n"); - fprintf(stderr, " sets the number of repetitions to run each benchmark\n"); - fprintf(stderr, " -w [# of warmup iterations]\n"); - fprintf(stderr, " sets the number of warmup runs for each benchmark\n"); - fprintf(stderr, " -o [output file name]\n"); - fprintf(stderr, " sets the name of the output file\n"); - fprintf(stderr, " -t [json|csv|console]\n"); - fprintf(stderr, " sets the output format of the output file\n"); - fprintf(stderr, " -p [file path to pem directory]\n"); - fprintf(stderr, " if using secure mode, must set pem directory\n"); - fprintf(stderr, " -g [google benchmark options]\n"); - fprintf(stderr, " sets the google benchmark options\n"); - fprintf(stderr, " -d #;#;#\n"); - fprintf(stderr, " sets the size of the data that should be sent in send/recv benchmarks\n"); - fprintf(stderr, " -s\n"); - fprintf(stderr, " run benchmarks in insecure mode\n"); - fprintf(stderr, " -D\n"); - fprintf(stderr, " print debug output to terminal\n"); - fprintf(stderr, "\n"); - exit(1); -} - -int benchmark_negotiate(struct s2n_connection *conn, int fd, benchmark::State& state, bool warmup) { - s2n_blocked_status blocked; - int s2n_ret; - if (!warmup) { - state.ResumeTiming(); - } - benchmark::DoNotOptimize(s2n_ret = s2n_negotiate(conn, &blocked)); //forces the result to be stored in either memory or a register. - if (!warmup) { - state.PauseTiming(); - } - benchmark::ClobberMemory(); //forces the compiler to perform all pending writes to global memory - - if (s2n_ret != S2N_SUCCESS) { - if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { - fprintf(stderr, "Failed to negotiate: '%s'. %s\n", - s2n_strerror(s2n_errno, "EN"), - s2n_strerror_debug(s2n_errno, "EN")); - fprintf(stderr, "Alert: %d\n", - s2n_connection_get_alert(conn)); - printf("errno: %s\n", strerror(errno)); - S2N_ERROR_PRESERVE_ERRNO(); - } - - if (wait_for_event(fd, blocked) != S2N_SUCCESS) { - S2N_ERROR_PRESERVE_ERRNO(); - } - - state.SkipWithError("Negotiate Failed\n"); - } - - if (DEBUG_PRINT) { - print_connection_info(conn); - } - - return 0; -} - -void argument_parse(int argc, char** argv, int& use_corked_io, int& insecure, char* bench_format, - std::string& file_prefix, long int& warmup_iters, size_t& iterations, size_t& repetitions, - std::string& gb_options, std::vector &data_sizes) { - while (1) { - int c = getopt(argc, argv, "ci:r:w:o:t:p:g:d:sD"); - if (c == -1) { - break; - } - switch (c) { - case 0: - /* getopt_long() returns 0 if an option.flag is non-null (Eg "parallelize") */ - break; - case 'c': - use_corked_io = 1; - break; - case 'i': - iterations = atoi(optarg); - break; - case 'r': - repetitions = atoi(optarg); - break; - case 'w': - warmup_iters = atoi(optarg); - break; - case 'o': - file_prefix = std::string(optarg); - break; - case 't': - strcat(bench_format, optarg); - break; - case 'p': - pem_dir = optarg; - break; - case 'd': - { - std::string s = std::string(optarg); - size_t pos = 0; - std::string token; - while ((pos = s.find(";")) != std::string::npos) { - token = s.substr(0, pos); - data_sizes.push_back(stoi(token)); - s.erase(0, pos + 1); - } - data_sizes.push_back(stoi(s)); - } - break; - case 's': - insecure = 1; - break; - case 'D': - DEBUG_PRINT = 1; - break; - case 'g': - gb_options = std::string(optarg); - break; - case '?': - default: - fprintf(stdout, "getopt returned: %d", c); - usage(); - break; - } - } - - if (optind < argc) { - host = argv[optind++]; - } - - if (optind < argc) { - port = argv[optind++]; - } -} diff --git a/tests/benchmark/utils/shared_info.h b/tests/benchmark/utils/shared_info.h deleted file mode 100644 index 42bbe742a67..00000000000 --- a/tests/benchmark/utils/shared_info.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 "api/s2n.h" -#include -#include -#include -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_cipher_preferences.h" - -extern struct s2n_cipher_suite **all_suites; -extern unsigned int num_suites; -extern int DEBUG_PRINT; -extern const char *host; -extern const char *port; -extern const char *pem_dir; -extern struct s2n_config *config; -extern struct conn_settings conn_settings; - -extern const char *rsa_certificate_chain; -extern const char *rsa_private_key; -extern const char *ecdsa_certificate_chain; -extern const char *ecdsa_private_key; - -int benchmark_negotiate(struct s2n_connection *conn, int fd, benchmark::State& state, bool warmup); -void argument_parse(int argc, char** argv, int& use_corked_io, int& insecure, char* bench_format, - std::string& file_prefix, long int& warmup_iters, size_t& iterations, size_t& repetitions, - std::string& gb_options, std::vector &data_sizes); From fc9ad9770ded19897f4463b0a127a8e049084fd7 Mon Sep 17 00:00:00 2001 From: kaukabrizvi <100529019+kaukabrizvi@users.noreply.github.com> Date: Tue, 13 Aug 2024 17:14:45 -0700 Subject: [PATCH 19/19] Modify regression threshold to configurable percentage (#4698) --- tests/regression/README.md | 10 ++++----- tests/regression/src/lib.rs | 44 ++++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/tests/regression/README.md b/tests/regression/README.md index 652d73cab39..8ee4ce3a707 100644 --- a/tests/regression/README.md +++ b/tests/regression/README.md @@ -5,7 +5,7 @@ This folder contains regression tests and benchmarking tools for the `s2n-tls` l ## Testing Philosophy -Currently, s2n-tls implements a wall clock benchmarking tool which measures end-to-end handshake performance to compare s2n-tls with rustls and OpenSSL. In the past, s2n-tls has tried benchmarking to detect regressions through criterion in Rust, but the subprocess and spin-up time contributed to performance measurement which made the results inaccurate and difficult to use in CI. The project has a slightly different focus, learning from these existing tools. Performance assertion in s2n-tls focuses on a benchmarking tool that can detail performance by API path and do so with enough repeatability and accuracy to detect regressions between two versions of s2n-tls so that performance analysis can occur at PR time. This means that the scope of each harness is limited and mutually exclusive of other harnesses since we are intersted in measuring the performance of the important paths a TLS connection typically follows. +Currently, s2n-tls implements a wall clock benchmarking tool which measures end-to-end handshake performance to compare s2n-tls with rustls and OpenSSL. In the past, s2n-tls has tried benchmarking to detect regressions through criterion in Rust, but the subprocess and spin-up time contributed to performance measurement which made the results inaccurate and difficult to use in CI. The project has a slightly different focus, learning from these existing tools. Performance assertion in s2n-tls focuses on a benchmarking tool that can detail performance by API path and do so with enough repeatability and accuracy to detect regressions between two versions of s2n-tls so that performance analysis can occur at PR time. This means that the scope of each harness is limited and mutually exclusive of other harnesses since we are interested in measuring the performance of the important paths a TLS connection typically follows. ### Why CPU instructions The performance benchmarking framework utilizes CPU Instruction count across API paths to make the regression assertion. This technique reduces noise, ensuring that small performance differences are caught through this measure. While a difference in performance count may not result in a direct runtime difference, it is useful when comparing a PR to mainline and to dig into the specific sources of performance impact within the code. @@ -33,7 +33,7 @@ To run the harnesses with Valgrind and store the annotated results, run: PERF_MODE=valgrind cargo test ``` -This will recursively call all tests with valgrind enabled so the performance output is generated and stored in target/perf_outputs. If you are looking for the scalar performance output of a PR, this will provide inisght into which portions of the code account for what share of the CPU instruction count. +This will recursively call all tests with valgrind enabled so the performance output is generated and stored in target/perf_outputs. If you are looking for the scalar performance output of a PR, this will provide insight into which portions of the code account for what share of the CPU instruction count. ## Running the Harnesses between versions (differential performance) Run the scalar performance for all harnesses on the current branch version of s2n-tls @@ -59,16 +59,16 @@ This will assert on the performance difference of the current version minus the cargo test ``` -This will run the tests without valgrind to test if the hanresses complete as expected +This will run the tests without valgrind to test if the harnesses complete as expected ## Output Files - `target/$commit_id/test_name.raw`: Contains the raw cachegrind profile. On its own, the file is pretty much unreadable but is useful for the cg_annotate --diff functionality or to visualize the profile via tools like [KCachegrind](https://kcachegrind.github.io/html/Home.html). -- `target/$commit_id/test_name.annotated`: The scalar annotated profile associated with that particular commit id. This file contains detailed infromation on the contribution of functions, files, and lines of code to the overall scalar performance count. +- `target/$commit_id/test_name.annotated`: The scalar annotated profile associated with that particular commit id. This file contains detailed information on the contribution of functions, files, and lines of code to the overall scalar performance count. - `target/diff/test_name.diff`: The annotated performance difference between two commits. This file contains the overall performance difference and also details the instruction counts, how many instructions a particular file/function account for, and the contribution of individual lines of code to the overall instruction count difference. ## Sample Output for Valgrind test (differential) -Running the differential test will run all harnesses and fail if any number of harnesses exceed the performance threshold. For example, a regression test faliure could look like: +Running the differential test will run all harnesses and fail if any number of harnesses exceed the performance threshold. For example, a regression test failure could look like: ``` running 2 tests test tests::test_set_config ... FAILED diff --git a/tests/regression/src/lib.rs b/tests/regression/src/lib.rs index 2794f5f8db5..04334ce3745 100644 --- a/tests/regression/src/lib.rs +++ b/tests/regression/src/lib.rs @@ -55,8 +55,6 @@ mod tests { process::{Command, Output}, }; - const MAX_DIFF: i64 = 1_000; - struct InstrumentationControl; impl InstrumentationControl { @@ -86,7 +84,11 @@ mod tests { } } - fn valgrind_test(test_name: &str, test_body: F) -> Result<(), s2n_tls::error::Error> + fn valgrind_test( + test_name: &str, + max_diff: f64, + test_body: F, + ) -> Result<(), s2n_tls::error::Error> where F: FnOnce(&InstrumentationControl) -> Result<(), s2n_tls::error::Error>, { @@ -97,7 +99,7 @@ mod tests { } RegressionTestMode::Diff => { let (prev_profile, curr_profile) = RawProfile::query(test_name); - DiffProfile::new(&prev_profile, &curr_profile).assert_performance(); + DiffProfile::new(&prev_profile, &curr_profile).assert_performance(max_diff); } RegressionTestMode::Default => { let ctrl = InstrumentationControl; @@ -144,6 +146,11 @@ mod tests { format!("target/{}/{}.raw", self.commit_hash, self.test_name) } + // Returns the annotated profile associated with a raw profile + fn associated_annotated_profile(&self) -> AnnotatedProfile{ + AnnotatedProfile::new(self) + } + /// Return the raw profiles for `test_name` in "git" order. `tuple.0` is older than `tuple.1` /// /// This method will panic if there are not two profiles. @@ -206,15 +213,23 @@ mod tests { fn path(&self) -> String { format!("target/{}/{}.annotated", self.commit_hash, self.test_name) } + + fn instruction_count(&self) -> i64 { + let output = &std::fs::read_to_string(self.path()).unwrap(); + find_instruction_count(output).unwrap() + } } struct DiffProfile { test_name: String, + prev_profile_count: i64, } impl DiffProfile { fn new(prev_profile: &RawProfile, curr_profile: &RawProfile) -> Self { let diff_profile = Self { test_name: curr_profile.test_name.clone(), + // reads the annotated profile for previous instruction count + prev_profile_count: prev_profile.associated_annotated_profile().instruction_count() }; // diff the raw profile @@ -225,6 +240,7 @@ mod tests { assert_command_success(diff_output.clone()); // write the diff to disk + create_dir_all(format!("target/diff")).unwrap(); let diff_content = String::from_utf8(diff_output.stdout) .expect("Invalid UTF-8 in cg_annotate --diff output"); write(diff_profile.path(), diff_content).expect("Failed to write to file"); @@ -236,21 +252,23 @@ mod tests { format!("target/diff/{}.diff", self.test_name) } - fn assert_performance(&self) { + fn assert_performance(&self, max_diff: f64) { let diff_content = std::fs::read_to_string(self.path()).unwrap(); - let diff = find_instruction_count(&diff_content) .expect("Failed to parse cg_annotate --diff output"); + // percentage difference is the overall difference divided by the previous instruction count + let diff_percentage = diff as f64 / self.prev_profile_count as f64; assert!( - diff <= MAX_DIFF, - "Instruction count difference exceeds the threshold, regression of {} instructions. - Check the annotated output logs in target/diff/{}.diff for debug information", - diff, self.test_name + diff_percentage <= max_diff, + "Instruction count difference exceeds the threshold, regression of {diff_percentage}% ({diff} instructions). + Check the annotated output logs in target/diff/{}.diff for debug information", self.test_name ); } + } - // Pulls the instruction count as an integer from the annotated output file. + /// Pulls the instruction count as an integer from the annotated output file. + /// Accepts output from Annotated and Diff profile formats. fn find_instruction_count(output: &str) -> Result { let reader = io::BufReader::new(output.as_bytes()); // Example of the line being parsed: @@ -303,7 +321,7 @@ mod tests { /// Test to create new config, set security policy, host_callback information, load/trust certs, and build config. #[test] fn test_set_config() { - valgrind_test("test_set_config", |ctrl| { + valgrind_test("test_set_config", 0.01, |ctrl| { ctrl.stop_instrumentation(); ctrl.start_instrumentation(); let keypair_rsa = CertKeyPair::default(); @@ -317,7 +335,7 @@ mod tests { /// Test which creates a TestPair from config using `rsa_4096_sha512`. Only measures a pair handshake. #[test] fn test_rsa_handshake() { - valgrind_test("test_rsa_handshake", |ctrl| { + valgrind_test("test_rsa_handshake", 0.01, |ctrl| { ctrl.stop_instrumentation(); let keypair_rsa = CertKeyPair::default(); let config = set_config(&security::DEFAULT_TLS13, keypair_rsa)?;